《算法竞赛入门经典》第二版课后习题
以下代码是个人原创,错误在所难免。如有错误欢迎斧正!
第二章课后习题
习题2-1:水仙花数(daffodil)
题目:
输出100~ 999中的所有水仙花数。 若3位数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, 所以153是水仙花数。
代码如下:
#include<stdio.h>
int main()
{
int n, m, sum = 0;
for(n = 100; n < 1000; n++)//暴力枚举求解
{
sum = 0;
m = n;
while(m){
sum += (m%10)*(m%10)*(m%10);
m = m/10;
}
if(sum==n) printf("%d\n", n);
}
return 0;
}
习题2-2:韩信点兵(hanxin)
题目:
相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠过一眼队伍的排尾就知道总人数了。输入包含多组数据,每组数据包含三个非负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>
int main()
{
int a, b, c, i;
int count = 1;//记录数据次数
while (scanf("%d%d%d", &a, &b, &c)==3){
for (i = 10; i <= 100; i++)//暴力枚举
if (i % 3 == a && i % 5 == b && i % 7 == c) break;
if (i > 100 || i < 10) { printf("Case %d:No answer\n", count); count++; }
else { printf("Case %d:%d\n", count, i); count++; }
}
return 0;
}
习题2-3:倒三角形(triangle)
题目:
输入正整数n<=20,输入一个n层的倒三角形。例如:n=5时输出如下:
#########
#######
#####
###
#
代码如下:
#include<stdio.h>
int main()
{
int i, j, k, n;
scanf("%d", &n);
for(i = n; i > 0; i--)//最外层循环控制行数
{
for(k = i; k < n; k++)//控制空格的输出
printf(" ");
for(j = 2*i-1; j > 0; j--)//控制每行#的个数
printf("#");
printf("\n");
}
return 0;
}
习题2-4:子序列的和(subsequence)
题目:
输入两个正整数
n
<
m
<
1
0
6
n<m<10^6
n<m<106,输出
1
/
n
2
+
1
/
(
n
+
1
)
2
+
…
…
+
1
/
m
2
1/n^2+1/(n+1)^2+……+1/m^2
1/n2+1/(n+1)2+……+1/m2,保留五位小数。输入包含多组数据,结束标记为n=m=0。提示:本题有陷阱。
样例输入:
2 4
65536 655360
0 0
样例输出:
Case 1:0.42361
Case 2:0.00001
代码如下:
#include<stdio.h>
int main()
{
int n, m;
int count = 1;//记录输入次数
while (scanf("%d%d", &n, &m)&&n&&m){
double sum = 0;
for (int i = n; i <= m; i++){
sum += 1 / ((double)i * (double)i);//本人认为陷阱就在此处,如果强制转换不得当,第二个测试样例会输出inf
}
printf("Case %d: %.5lf\n", count++, sum);
}
return 0;
}
习题2-5:分数化小数(decimal)
题目:
输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。a,b≤106,c≤100。输入包含多组数据,结束标记为a=b=c=0。
样例输入:
1 6 4
0 0 0
样例输出:
Case 1: 0.1667
代码如下:
#include<stdio.h>
int main()
{
int a, b, c;
int count = 1;//记录输入次数
while (scanf("%d%d%d", &a, &b, &c)&&a&&b&&c){
double sum = 0;
//要进行强制转换
sum = (double)a / (double)b;
//可以使用变量来控制精度,用*来代表该变量,宽度可以如此使用
printf("Case %d: %.*lf\n", count++, c, sum);
}
return 0;
}
习题2-6:排列(permutation)
题目:
用1,2,3,…,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3。按照“abc def ghi”的格式输出所有解,每行一个解。提示:不必太动脑筋。
代码如下:
#include<stdio.h>
//写一个求各个位数之和和之积的函数
void func(int num, int *sum1, int *sum2)
{
while (num)
{
*sum1 += num % 10;
*sum2 *= num % 10;
num /= 10;
}
}
int main()
{
int abc, def, ghi;
//依次求解每一种可能性
for (int abc = 123; abc <= 329; abc++)
{
int sum1 = 0, sum2 = 1;
def = abc * 2;
ghi = abc * 3;
//求每一个数字各个位数上的和与积
func(abc, &sum1, &sum2);
func(def, &sum1, &sum2);
func(ghi, &sum1, &sum2);
//1-9之间的数和为45,积为362880,可以作为判定条件
if (sum1 == 45 && sum2 == 362880) {
printf("%d %d %d\n", abc, def, ghi);
}
}
return 0;
}
第三章课后习题
习题3-1:得分(Score)
题目:
给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。
代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
int count = 0, sum = 0;
char str[90];
scanf("%s", str);
int n = strlen(str);
for (int i = 0; i < n; i++) {
//对每个字符进行判断
if (str[i] == 'O') {
count++;
sum += count;
}
else
count = 0;
}
printf("%d\n", sum);
}
习题3-2:分子量(Molar Mass)
题目:
给出一种物质的分子式(不带括号),求分子量(结果保留三位小数)。本题中的分子式只包含4种原子,分别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol)。例如,C6H5OH的分子量为94.108g/mol。
代码如下:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
double sum = 0;
double num[4] = { 12.01, 1.008, 16.00, 14.01 };
char alpha[5] = { 'C', 'H', 'O', 'N', '\0' };
char str[90]; //查了原题目,字符小于80个
int count = 0, j; //count用于统计字符串中数字的大小
scanf("%s\n", str);
int n = strlen(str);
for (j = 0; j < 4; j++) { //先找到第一个字母对应的j值
if (alpha[j] == str[0])
break;
}
for (int i = 1; i < n; i++) {
//如果是数字,则对count进行处理
if (isdigit(str[i])) {
count = count*10 + (str[i] - '0');
}
//字母的话则检测count的值并对sum进行相应值的增加
else {
if (count) { sum += count * num[j]; }
else sum += num[j];
count = 0;
//继续寻找当前字母的位置
for (j = 0; j < 4; j++) { if (alpha[j] == str[i]) break; }
}
}
//对最后一个字符进行处理的情况
if (count == 0)//若最后一个字符为字母,则count为1
count = 1;
sum += count * num[j];
printf("%.3lf\n", sum);
}
习题3-3:数数字(Digit Counting)
题目:
把前n(n≤10000)个整数顺次写在一起123456789101112…数一数0~9各出现多少次(输出10个整数,分别是0,1,…,9出现的次数)。
代码如下:
/*这道题的思路也很简单,把数字当成字符依次接受,并创建一个计数的数组来记录0-9出现的次数*/
#include<stdio.h>
#include<string.h>
int main()
{
char c;
int cnt[10] = { 0 };
while ((c = getchar()) != '\n') //遇到回车后停止输入
cnt[c - '0']++; //相应数字对应的数组
for (int i = 0; i < 10; i++) //输出每个数字出现的次数
printf("%d ", cnt[i]);
}
习题3-4:周期串(Periodic Strings)
题目:
如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。输入一个长度不超过80的字符串,输出其最小周期。
代码如下:
/*因为字符串比较小,所以我们可以直接使用枚举的办法求解最小周期*/
#include<stdio.h>
#include<string.h>
int main()
{
char a[80];
fgets(a, 80, stdin);//防止字符串中出现空格
int n = strlen(a);
for (int i = 1; i < n; i++) { //设每次的周期为i
//这里要注意以下,换行也会当作一个字符存储在a中,所以判断的时候要往前移一位
for (int j = 0; j < n - 1; j++) {
if (a[j] != a[j % i]) //判断后面的每个字符是否与周期串相同
break;
if (j == n - 2) {
printf("%d\n", i);
return 0;
}
}
}
}
习题3-5:谜题(Puzzle)
题目:
有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母。一共有4种指令:A, B, L, R,分别表示把空格上、下、左、右的相邻字母移到空格中。输入初始网格和指令序列(以数字0结束),输出指令执行完毕后的网格。如果有非法指令,应输出“Thispuzzle has no final configuration.”,例如,图3-5中执行ARRBBL0后,效果如图3-6所示。
代码如下:
/*按照数组方式存入数据,然后对每个指令进行相应的操作即可*/
#include<stdio.h>
#include<string.h>
int main()
{
char a[5][5];
//输入网格内的数据
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
a[i][j] = getchar();
}
getchar();//吸收换行
}
char c; //每次的指令
int x, y; //记录空格的位置
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
if (a[i][j] == ' '){
x = i;
y = j;
}
//开始接受指令
while ((c = getchar()) != '0')
{
if (c == 'A') { a[x][y] = a[x - 1][y]; a[x - 1][y] = ' '; x = x - 1; }
else if(c == 'B') { a[x][y] = a[x + 1][y]; a[x + 1][y] = ' '; x = x + 1; }
else if(c == 'L') { a[x][y] = a[x][y - 1]; a[x][y - 1] = ' '; y = y - 1; }
else if(c == 'R') { a[x][y] = a[x][y + 1]; a[x][y + 1] = ' '; y = y + 1; }
else { printf("This puzzle has no final configuration."); return 0; }
}
//输出最终结果
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
printf("%c", a[i][j]);
}
printf("\n");
}
}
习题3-6:纵横字谜的答案(Crossword Answers)
题目:
输入一个r行c列(1≤r,c≤10)的网格,黑格用“*”表示,每个白格都填有一个字母。如果一个白格的左边相邻位置或者上边相邻位置没有白格(可能是黑格,也可能出了网格边界),则称这个白格是一个起始格。首先把所有起始格按照从上到下、从左到右的顺序编号为1, 2, 3,…,如图3-7所示。
接下来要找出所有横向单词(Across)。这些单词必须从一个起始格开始,向右延伸到一个黑格的左边或者整个网格的最右列。最后找出所有竖向单词(Down)。这些单词必须从一个起始格开始,向下延伸到一个黑格的上边或者整个网格的最下行。输入输出格式和样例请参考原题。
代码如下:
待更新
习题3-7:DNA序列(DNA Consensus String)
题目:
输入m个长度均为n的DNA序列,求一个DNA序列,到所有序列的总Hamming距离尽量小。两个等长字符串的Hamming距离等于字符不同的位置个数,例如,ACGT和GCGA的Hamming距离为2(左数第1, 4个字符不同)。
输入整数m和n(4≤m≤50, 4≤n≤1000),以及m个长度为n的DNA序列(只包含字母A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。如有多解,要求为字典序最小的解。例如,对于下面5个DNA序列,最优解为TAAGATAC。
代码如下:
待更新
习题3-8:循环小数(Repeating Decimals)
题目:
输入整数a和b(0≤a≤3000,1≤b≤3000),输出a/b的循环小数表示以及循环节长度。例如a=5,b=43,小数表示为0.(116279069767441860465),循环节长度为21。
代码如下:
待更新
习题3-9:子序列(All in All)
题目:
输入两个字符串s和t,判断是否可以从t中删除0个或多个字符(其他字符顺序不变),
得到字符串s。例如,abcde可以得到bce,但无法得到dc。
代码如下:
待更新
习题3-10:盒子(Box)
题目:
给定6个矩形的长和宽
w
i
w_i
wi和
h
i
h_i
hi(
1
≤
w
i
,
h
i
≤
1000
1≤w_i,h_i≤1000
1≤wi,hi≤1000),判断它们能否构成长方体的6个面。
样例输入:
1234 4567
1234 4567
4567 4321
4322 4567
4321 1234
4321 1234
样例输出:
IMPOSSIBLE
代码如下:
待更新
习题3-11:换低挡装置(Kickdown)
题目:
给出两个长度分别为
n
1
n_1
n1,
n
2
n_2
n2(
n
1
n_1
n1,
n
2
n_2
n2≤100)且每列高度只为1或2的长条。需要将它们放入一个高度为3的容器(如图3-8所示),问能够容纳它们的最短容器长度。
代码如下:
待更新
习题3-12:浮点数(Floating-Point Numbers)
题目:
计算机常用阶码-尾数的方法保存浮点数。如图3-9所示,如果阶码有6位,尾数有8位,可以表达的最大浮点数为
0.11111111
1
2
×
2
2
111111
0.111111111_2×2_{2}^{111111}
0.1111111112×22111111。注意小数点后第一位必须为1,所以一共有9位小数。
这个数换算成十进制之后就是
0.998046875
∗
2
63
=
9.205357638345294
∗
1
0
18
0.998046875*2^ {63}=9.205357638345294*10^{18}
0.998046875∗263=9.205357638345294∗1018。你的任务是根据这个最大浮点数,求出阶码的位数
E
E
E和尾数的位数
M
M
M。输入格式为
A
e
B
AeB
AeB,表示最大浮点数为
A
∗
1
0
B
A*10_B
A∗10B。
0
<
A
<
10
0<A<10
0<A<10,并且恰好包含15位有效数字。输入结束标志为
0
e
0
0e0
0e0。对于每组数据,输出
M
M
M和
E
E
E。输入保证有唯一解,且
0
≤
M
≤
9
0≤M≤9
0≤M≤9,
1
≤
E
≤
30
1≤E≤30
1≤E≤30。在本题中,
M
+
E
+
2
M+E+2
M+E+2不必为8的整数倍。
代码如下:
待更新