算法竞赛入门经典(第2版)
2.5 注解与习题
2-1 水仙花数(daffodi)
输出100~999中所有水仙花数。若三位数ABC满足 A B C = A 3 + B 3 + C 3 ABC=A^3+B^3+C^3 ABC=A3+B3+C3,则称其为水仙花数。例如 153 = 1 3 + 5 3 + 3 3 。 153=1^3+5^3+3^3。 153=13+53+33。
//输出100~999中所有水仙花数
#include<stdio.h>
int main(){
int a,b,c;
for(int i=100;i<1000;i++){
a = i/100;
b = (i - a*100)/10;//i/10 - a*10;
c = i%10;
if(a*100+b*10+c == a*a*a+b*b*b+c*c*c){
//printf("i = %d\n",i);
printf("%d ",a*a*a+b*b*b+c*c*c);
}
}
// for(a=1;a<10;a++){
// for(b=0;b<10;b++){
// for(c=0;c<10;c++){
// if(a*100+b*10+c == a*a*a+b*b*b+c*c*c){
// printf("%d ",a*100+b*10+c);
// }
// }
// }
// }
return 0;
}
2-2 韩信点兵(hanxin)
士兵先后以三人一排、五人一排、七人一排地变换队形,韩信每次只看队伍排尾就知道总人数。输入包含多组数据,每组数据包含3个非负整数a,b,c
,表示每种队形排尾的人数(a<3, b<5, c<7),输出总数的最小值(或报告无解)。已知总人数不小于10,不超过100.输入到文件结束为止。
样例输入:
2 1 6
2 1 3
样例输出
Case 1: 41
Case 2: No answer
//韩信点兵
#include<stdio.h>
typedef enum{false,true}bool;
int main(){
int a,b,c;
bool find_answer; //标志位
int count = 0; //计数器,每输入一组数,count加1
while(scanf("%d%d%d",&a,&b,&c) == 3){
find_answer = false;
printf("Case %d:",count++);
for(int i=10;i<=100;i++){
if((i-a)%3==0 && (i-b)%5==0 && (i-c)%7==0){
find_answer = true;
printf("%d\n",i);
}
}
if(!find_answer){
printf("No answer\n");
}
}
return 0;
}
2-3倒三角(triangle)
输入正整数n<=20,输出一个n层的倒三角型。
“当输入n=5时”
输出
*********
*******
*****
***
*
代码
//triangle
//Input:n
//Output:n层倒三角
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
for(int j=0;j<n*2-1 - i;j++){
if(j<i)
printf(" ");
else
printf("*");
}
printf("\n");
}
}
思路:
- 倒三角从下到上按照1, 3, 5,7… n × 2 + 1 n\times2+1 n×2+1递增
- 打印一个长方形
for(int i=0;i<n;i++){
for(int j=0;j<n*2-1 ;j++){
printf("*");
}
*********
*********
*********
*********
*********
- 每层打印的
*
数量减1,去掉右下的三角形
for(int i=0;i<n;i++){
for(int j=0;j<n*2-1 - i;j++){
printf(" ");
}
*********
********
*******
******
*****
- 第i层,前i个
*
替换成空格,去掉左下的三角形
for(int i=0;i<n;i++){
for(int j=0;j<n*2-1 - i;j++){
if(j<i)
printf(" ");
else
printf("*");
}
*********
*******
*****
***
*
2021.3.11
2-4子序列的和(subsequence)
输入两个·正整数
n
<
m
<
1
0
6
n<m<10^6
n<m<106 输出
1
n
2
+
1
n
2
+
1
+
.
.
.
+
1
m
2
\frac{1}{ n^2}+\frac{1}{ n^2+1}+...+\frac{1}{ m^2}
n21+n2+11+...+m21
保留5位小数。输入包含多组数据,结束标记为n=m=0。提示:本题有陷阱
样例输入
2 4
65536 655360
0 0
样例输出
Case 1:0.42361
Case 2:0.00001
代码
//子序列的和(subsequence)
#include<stdio.h>
int main(){
int a,b;
long double s;
int count = 1;
while(scanf("%d%d",&a,&b)==2){
s = 0;
if(a==0 && b==0) break;
else if(a<b){
for(int i=a;i<=b;i++){
//printf("s=%Lf\t",s);
//printf("i=%d\t",i);
//s += 1.0/(i*i*1.0);
s += (1.0/i)*(1.0/i);
}
//printf("\n");
printf("Case %d:%.5Lf\n",count++,s);
}else{
return 1;
}
}
return 0;
}
65536655360的值超出了int的范围*
65536*655360 = 42949672960
2**32 = 4294967296
2**64 = 18446744073709551616
2-5 分数化小数
输入正整数a, b, c, 输出a/b的小数形式,精确到小数点后c位。
a
,
b
≤
1
0
6
,
c
≤
100
。
a,b\le10^6,c\le100。
a,b≤106,c≤100。
数入包含多组数据,结束标记为a=b=c=0。
样例数入:
1 6 4
0 0 0
样例输出:
Case 1:0.1667
代码
// 2-5 分数化小数(decimal)
#include<stdio.h>
int main(){
int a,b,c;
int count = 1;
// 输入正整数a, b, c
while(scanf("%d%d%d",&a,&b,&c)==3){
if(a==0 && b==0 && c==0) break; // 结束标记为a=b=c=0
else if(a>0 && b>0 && c>0){
printf("Case %d:%.*f\n",count++,c,a*1.0/b);
}else return 1;
}
return 0;
}
2-6 排列(permutation)
用1,2,3,…,9组成3个三位数abc, def, ghi, 每个数字恰好使用一次,要求abc:def:ghi = 1:2:3。按照 “abc def ghi” 的格式输出所有解,每行一个解。提示:不必太动脑筋。
代码
// 2-6 排列(permutation)
#include<stdio.h>
int main(){
int a,b,c;
int array[9];
int temp;
int count;
int flag_right = 0; // 标志位
for(a=1;a<10;a++){
for(b=1;b<10;b++){
if(a==b) continue;
for(c=1;c<10;c++){
count = 0; // 记录有无重复数字
if(c==b || c== a) continue;
temp = a*100+b*10+c;
if(temp*3>999) continue;
array[0] = a;
array[1] = b;
array[2] = c;
array[3] = (temp*2)/100;
array[4] = (temp*2)/10 - array[3]*10;
array[5] = (temp*2)%10;
array[6] = (temp*3)/100;
array[7] = (temp*3)/10 - array[6]*10;
array[8] = (temp*3)%10;
for(int i=0;i<9;i++){
for(int j=i;j<9;j++){
if(i==j) continue;
if(array[j] == array[i] || array[j]==0){ //出现重复数字或者0则退出
count++;
break;
}
}
if(count>0) break; // 有数字重复
}
if(count == 0) printf("%d %d %d\n",temp,temp*2,temp*3);
}
}
}
return 0;
}
运行结果
192 384 576
219 438 657
273 546 819
327 654 981
思路
很自然的想法是对temp = abc 做乘法得到其余数字,用数组将这些数字保存然后遍历查看无重复数字,记得数字中不能有0。
思考题
题目1
假设需要输出2,4,6,8,…,2n, 每个一行,能不能通过对程序2-1进行小小的改动来实现呢?为了方便,现在把程序复制如下:
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
printf("%d\n",i);
return 0;
}
任务1:修改第7行,不修改第6行
任务2:修改第6行,不修改第7行
代码
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=n*2;i*=2) // 任务2
printf("%d\n",i/*2*/); // 任务1: i -> i*2
return 0;
}
题目2
下面的程序运行结果是什么? “!=” 算法运算符表示不相等。提示:请上机实验,不要凭主观感觉回答。
#include<stdio.h>
int main()
{
double i;
for(i=0;i!=10;i+=0.1)
printf("%.1f\n",i);
return 0;
}
直觉上好像会打印0.0, 0.1, …, 9.9,但实际上会无限打印下去,因为double是浮点数,它无法精确地等于整型10。
4489.4
4489.5
4489.6
4489.7
4489.8
4489.9
4490.0
4490.1
4490.2
4490.3
4490.4
4490.5
4490.6
测试
将修改代码为:
#include<stdio.h>
int main()
{
double i;
for(i=0;i!=10;i+=0.1){
if ((int)i>=10){
printf("(int)i = %d",(int)i);
break;
}
printf("%.1f\n",i);
}
return 0;
}
可以看到打印结果:
9.4
9.5
9.6
9.7
9.8
9.9
10.0
(int)i = 10
当i等于10.0时,不等式(int)i==10
并不成立,我们知道用(int)
强制类型转换会丢弃掉所有小数部分,由此可知当i的打印只是10.0的时候,它的实际值是小于10的,比如9.9999999。