【深入浅出程序设计竞赛(基础篇)第四章 算法从0开始】

深入浅出程序设计竞赛(基础篇)第四章 算法从0开始

第四章 例题

例4-1

cpp 复制代码
#include<iostream>
using namespace std;
int main(){
    int L, i; cin >> L;
    for (i = 1; i <= L; i++) {
        cout << "Today, I ate " << i << " apple";
        if (i != 0 && i != 1) cout << "s";
        cout << "." << endl;
    }
    return 0;
}

例4-2

cpp 复制代码
#include<iostream>
using namespace std;
int main(){
    int n, tmp, minnum = 100000000;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> tmp;
        if (tmp < minnum) minnum = tmp;
    }
    cout << minnum << endl;
    return 0;
}

例4-3

更简单的解法

cpp 复制代码
#include<cstdio>
using namespace std;
int main(){
    int n, k, i;
    int Asum = 0, Bsum = 0;
    scanf("%d%d", &n, &k);
    if (n < k) {
        Asum = 0;
    }else {
        Asum = (n / k + 1) * (n / k) / 2 * k;
    }
    Bsum = (n + 1) * n / 2 - Asum;
    printf("%.1f %.1f",  double(Asum) / (n / k), double(Bsum) / (n - n / k));
    return 0;
}

书上解法

cpp 复制代码
#include<cstdio>
using namespace std;
int main(){
    int n, k, i;
    int Asum = 0, Bsum = 0;
    scanf("%d%d", &n, &k);
    for (i = k; i <= n; i += k) {
        Asum += i;
    }
    Bsum = (1 + n) * n / 2 - Asum;
 printf("%.1f %.1f",  double(Asum) / (n / k), double(Bsum) / (n - n / k)); //%lf仅用于读入double类型变量而不用于输出
    return 0;
}

例4-4

cpp 复制代码
#include<iostream>
using namespace std;
int main(){
    int a, days = 1;
    cin >> a;
    while(a > 1)
        days++, a /= 2;
    cout << days;
    return 0;
}

例4-5

cpp 复制代码
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
int main(){
    int ans, guess;
    srand(time(0));
    ans = rand() % 100 + 1;
    cout << ans;
    do {
        cin >> guess;
        if (guess < ans) 
            cout << "Too small" << endl;
        if (guess > ans)
            cout << "Too large" << endl;
    }while(ans != guess);
    cout << "You are right!!!" << endl;
    return 0;
}

例4-6

cpp 复制代码
#include<cstdio>
using namespace std;
int main(){
    int cnt = 0, n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n - i + 1; j++) {
            printf("%02d", ++cnt);
        }
        printf("\n");
    }
    return 0;
}

例4-7

这道题书上的题解并不能解答洛谷P1009的题目,等后面学到第八章再来更新答案

cpp 复制代码
#include<iostream>
using namespace std;
int main(){
    long long n, ans = 0;
    cin >> n;
    for(int i = 1; i <= n; i++){
        long long factor = 1;
        for(int j = 1; j <= i; j++){
            factor *= j;
        }
        ans += factor;
    }
    cout << ans << endl;
}

也可以采用递归

cpp 复制代码
#include<iostream>
using namespace std;

long long calc(int n){
    if(n == 1)
        return 1;
    else
        return calc(n-1) * n;
}

int main(){
    long long n, ans = 1;
    cin >> n;
    for(int i = 2; i <= n; i++){
        ans += calc(i);
    }
    cout << ans << endl;
}

例4-8

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n, x, ans = 0;
    cin >> n >> x;
    for(int i = 1; i <= n; i++){
        int tmp = i, num;
        while(tmp != 0){
            num = tmp % 10;
            if(num == x)
                ans++;
            tmp /= 10;
        }
    }
    cout << ans;
    return 0;
}

例4-9

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int k, ans = 0;
    cin >> k;
    for(double Sn = 0; Sn <= k; ans++, Sn += 1.0 / ans);
    cout << ans;
    return 0;
}

例4-10

书上解法

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int k, coin = 0, day = 0;
    cin >> k;
    for(int i = 1;; i++){
        for(int j = 1; j <= i; j++){
            coin += i; day++;
            if(day == k){
                cout << coin << endl;
                return 0;
            }
        }
    }
    return 0;
}

我的解法

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int k, ans = 0, i;
    cin >> k;
    for(i = 1; i <=k; i++){
        ans += i * i; //加上当前天数的金币
        k -= i; //保证剩下还有天数
    }
    ans += i * k; //加上剩下天数的钱币
    cout << ans;
    return 0;
}

例4-11

解法一

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n, s = 0;
    cin >> n;
    for(int i = 1; i <= n; i++){
        s += i;
    };
    cout << s;
    return 0;
}

解法二

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int s = 0, i = 0, n;
    cin >> n;
    while(n--) s += ++i;
    cout << s;
    return 0;
}

例4-12

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    double n, s = 0;
    cin >> n;
    for(int i = 1; i <= n; i++){
        s += i * 0.1;
    };
    cout << s;
    return 0;
}

错误代码,注意精度

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n;
    double s = 0;
    cin >> n;
    for(double i = 0.1; i != n; i += 0.1){
        s += i; 
        // cout << s << endl;
    }
    cout << s;
    return 0;
}

例4-13

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int L, load = 0, ans = 0;
    cin >> L;
    for(int i = 2; ; i++){
        int is_prime = 1;
        // for(int j = 2; j * j <= i; j++){ //正常写应该是j <= sqrt(i),改为乘法加快运算速度
            if(i % j == 0){
                is_prime = 0;
                break;
            }
        }
        if(!is_prime) continue;
        if(i + load > L) break;
        cout << i << endl;
        ans++; load += i;
    }
    cout << ans;
    return 0;
}

例4-14

解题思路:

1、因为回文数的数量很少,所以先构造回文数

题目数据范围:5≤a<b≤100,000,000

  • 数据位数可以是1、2、3、4、5、6、7、8、9
  • 位数为1且>=5的回文数质数:5、7
  • 位数为2的回文数质数:11
  • 位数为4、6、8的回文数都是11的整数倍不是质数,不予考虑
  • 位数为3、5、7的回文数:自己构造
  • 位数为9的数只有一个100000000显然不满足条件,不予考虑

2、再判断是否是质数

3、如果符合条件则输出

cpp 复制代码
#include<iostream>
using namespace std;

int is_prime(int x){
    if(x % 2 == 0) return 0; //偶数一定不是质数
    for(int i = 3; i * i <= x; i++){
        if(x % i == 0) return 0;
    }
    return 1;
}

int main(){
    int a, b;
    cin >> a >> b;
    //回文数奇数位为1时 只有 2 3 5 7满足是质数,本题范围>=5,所以只有5 7
    if(a <= 5 && b >= 5) cout << 5 << endl;
    if(a <= 7 && b >= 7) cout << 7 << endl;
    // 回文数位数为偶数时,只有11为质数,其他都是11的倍数,如1001、1221、345543
    if(a <= 11 && b >= 11) cout << 11 << endl;

    //3、5、7枚举
    //三位数的回文数
    for(int d1 = 1; d1 <= 9; d1 += 2){ //开头数字不能为0,偶数一定不是质数
        for(int d2 = 0; d2 <= 9; d2++){
            int num = 100 * d1 + 10  * d2 + d1;
            if(num < a) continue;
            if(num > b) return 0;
            if(is_prime(num)) cout << num << endl;
        }
    }

    //五位数的回文数
    for(int d1 = 1; d1 <= 9; d1 += 2){
        for(int d2 = 0; d2 <= 9; d2++){
            for(int d3 = 0; d3 <= 9; d3++){
                int num = 10000 * d1 + 1000*d2 + 100 * d3 + 10 * d2 + d1;
                if(num < a) continue;
                if(num > b) return 0;
                if(is_prime(num)) cout << num << endl;
            }
        }
    }

     //七位数的回文数
    for(int d1 = 1; d1 <= 9; d1 += 2){
        for(int d2 = 0; d2 <= 9; d2++){
            for(int d3 = 0; d3 <= 9; d3++){
                for(int d4 = 0; d4 <= 9; d4++){
                    int num = 1000000 * d1 + 100000*d2 + 10000 * d3 + 1000 * d4 + 100 * d3 + 10 * d2 + d1;
                    if(num < a) continue;
                    if(num > b) return 0;
                    if(is_prime(num)) cout << num << endl;
                }
            }
        }
    }

    return 0;
}

第四章 课后习题

4-1

使用c++自带的库函数

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int a[5] = {11, 3, 5, 90, 20};
    sort(a, a+5);
    cout << a[4];
    return 0;
}

自己写排序算法,如冒泡排序

做了两处优化:1、循环次数优化 2、flag标志减少排序次数

cpp 复制代码
#include<iostream>
using namespace std;
int main(){
    int a[5] = {11, 3, 5, 90, 20}, temp, flag;
    for(int i = 0; i < 5 - 1; i++){
        for(int j = 0; j < 5 - 1 - i; j++){
            flag = 0;
            if(a[j] > a[j + 1]){
                temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                flag = 1;
            }
        }
        if(!flag) break; //如果flag没有改变,则说明数据已经排好序,无需继续排序
    }
    for(int i = 0; i < 5; i++) cout << a[i] << endl;
    return 0;
}

4-2

等比数列求和公式s = a1 * (1 - q ^ n) / (1 - q)

反推n = log(1 - (1 - q) / 2 * s) / log(q)

题目中n全部都是整数,也就是说,宁可多走一步步,让实际路程>=要求路程

因此在上式中,一旦n算出来是带小数的,就得把它进一位,因此再加一个ceil函数

a1 = 2, q = 0.98,即n = ceil(log(1 - 0.01 * s) / log(0.98))

cpp 复制代码
#include<iostream>
#include<cmath>
using namespace std;
int main(){
    float s, sum = 2;
    cin >> s;
    int ans;
    ans = ceil(log(1 - 0.01 * s) / log(0.98));
    cout << ans;
    return 0;
}

4-3

巧妙之处在于可以用字符串接收数字会很方便反转,对对应位直接进行加减也就没有了前置0 问题了

cpp 复制代码
#include<iostream>
#include<cmath>
using namespace std;

int main(){
    string a;
    cin >> a;
    long long ans = 0;
    if(a[0] == '-'){
        for(int i = a.length() - 1; i >= 1; i--){
            ans -= (a[i] - '0') * pow(10, i-1);
        }
    }else{
        for(int i = a.length() - 1; i >= 0; i--){
           ans += (a[i] - '0') * pow(10, i);
        }
    }
    cout << ans;
    return 0;
}

4-4

斐波那契数列

解法一:递归

但是洛谷超时

cpp 复制代码
#include<cstdio>
using namespace std;

float cum(int n){
    if(n == 0){
        return 0;
    }else if(n == 1 || n == 2){
        return 1;
    }
    else{
        return cum(n-1) + cum(n-2);
    }
}

int main(){
    int n;
    scanf("%d", &n);
    float ans = cum(n);
    printf("%.2f", ans);
    return 0;
}

解法二:

递推

cpp 复制代码
#include<cstdio>
using namespace std;

int main(){
    int n;
    scanf("%d", &n);
    double f[50] = {0, 1, 1};
    for(int i = 3; i <= n; i++){
        f[i] = f[i - 1] + f[i - 2];
    }
    printf("%.2lf", f[n]);
    return 0;
}

4-5

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n;
    cin >> n;
    int temp, min = 1001, max = -1;
    for(int i = 0; i < n; i++){
        cin >> temp;
        if(temp < min) min = temp;
        if(temp > max) max = temp;
    }
    cout << max - min;
    return 0;
}

4-6

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n;
    cin >> n;
    int f[n];
    for(int i = 0; i < n; i++){
        cin >> f[i];
    }
    int c = 1, ans = 1;
    for(int i = 0; i < n - 1; i++){
        if(f[i + 1] - f[i] == 1) c++;
        else c = 1;
        if(c > ans) ans = c;
    }
    cout << ans;
    return 0;
}

4-7

唯一分解定理: 一个数能且只能分解为一组质数的乘积。

可知,若输入的数满足题目条件,他就只能分解为两个质数的乘积。所以在比他小且大于1的自然数中,只有那两个数能整除它,之间不可能再有任何合数或质数能整除它了,因为最小的能整除它的合数已经是他本身了。

cpp 复制代码
#include<iostream>
using namespace std;

int main(){
    int n;
    cin >> n;
    for(int i = 2; i < n; i ++){
        if(n % i == 0){
            cout << n / i;
            break;
        }
    }
    return 0;
}

4-8

cpp 复制代码
#include<cstdio>

int main(){
    int n;
    scanf("%d", &n);

    int c = 1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            printf("%02d", c);
            c++;
        }
        printf("\n");
    }

    printf("\n");

    c = 1;
     for(int i = 1; i <= n; i++){
        for(int k = 1; k <= 2 * n - 2 * i; k++){
            printf(" " );
        }
        for(int j = 1; j <= i; j++){
            printf("%02d", c);
            c++;
        }
        printf("\n");
    }
    return 0;
}

4-9

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    int a[n];
    for(int i = 0; i < n; i++){
       cin >> a[i];
    }
    sort(a, a+n);
    double ans;
    for(int i = 1; i < n - 1; i++){
        ans += a[i];
    }
    printf("%.2lf", ans / (n - 2));
    return 0;
}

4-10

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    int start = n / 364;
    if(start > 100) start = 100;
    for(int i = start; i >= 1; i--){
        int k = 1;
        while(k > 0){
            if((i + 3 * k) * 364 == n){
                cout << i << endl;
                cout << k << endl;
                return 0;
            }
            else if((i + 3 * k) * 364 > n){
                break;
            }
            k++;
        }
    }
    return 0;
}

4-11

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main(){
    int cost = 0, balance = 0, money = 0;
    for(int i = 1; i <= 12; i++){
        cin >> cost;    
        balance += 300 - cost;
        if(balance < 0){
            cout << -i;
            return 0;
        }
        else if(balance % 100 >= 0){
            money +=  balance - balance % 100;
            balance = balance % 100;
        }
    }
    cout << money * 1.2 + balance;
    return 0;
}
相关推荐
我们的五年2 分钟前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
张彦峰ZYF2 分钟前
投资策略规划最优决策分析
分布式·算法·金融
The_Ticker17 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
程序猿阿伟18 分钟前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链
Lenyiin1 小时前
02.06、回文链表
数据结构·leetcode·链表
爪哇学长1 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
爱摸鱼的孔乙己1 小时前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
Dola_Pan1 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
烦躁的大鼻嘎1 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode