《算法竞赛入门经典》第二版课后习题(持续更新)

《算法竞赛入门经典》第二版课后习题

以下代码是个人原创,错误在所难免。如有错误欢迎斧正!

第二章课后习题

习题2-1:水仙花数(daffodil)
题目:
输出100~ 999中的所有水仙花数。 若3位数ABC满足 A B C = A 3 + B 3 + C 3 ABC= A^3+ B^3+ C^3 ABCA3B3C3, 则称其为水仙花数。 例如 153 = 1 3 + 5 3 + 3 3 153=1^3 +5^3+ 3^3 153135333, 所以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 1wihi1000),判断它们能否构成长方体的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.998046875263=9.2053576383452941018。你的任务是根据这个最大浮点数,求出阶码的位数 E E E和尾数的位数 M M M。输入格式为 A e B AeB AeB,表示最大浮点数为 A ∗ 1 0 B A*10_B A10B 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 0M9 1 ≤ E ≤ 30 1≤E≤30 1E30。在本题中, M + E + 2 M+E+2 M+E+2不必为8的整数倍。

代码如下:

待更新
  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值