深入浅出程序设计竞赛(基础篇)第四章 算法从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;
}