C++编程入门十题
题1:数塔
题2:数字排序问题
题3:日历问题
题4:约瑟夫问题
题6:选美比赛
题7:在霍格沃茨找零钱
题8:数圈
题9:小A的计算器
题10:找出直系亲属
给定一个数塔,如下图所示。在此数塔中,从顶部出发,在每一节点可以选择走左下或右下,一直走到底层。请找出一条路径,使路径上的数值和最大。
9
12 15
10 6 8
2 18 9 5
19 7 10 4 16
这是一个求最大权值的问题,也就是说相当于问从顶层到底层的一个最大生成树的权值。我们可以从底层出发,每两个进行判断,将较大值传递到上面一层并标记,这样逐层传递,也就相当于把问题简化到先以最后一层为底层、倒数第二层为顶层,再以得到一次值传递的倒数第二层为底层、倒数第三层为为顶层······这样一直计算到顶层,则此时顶层已经得到了由下面所有层中的最大权值,此时顶层即为所求。而在计算中,我们也对路径进行了标记,在输出时,就只需输出得到最大权值的路径上若干个数值。
一个简单的求最优解的问题,使用传值的方式实现题目要求的权值计算,然后将对应最大权值的路径上的被标记的数值输出。
1. 主模块设计
a. 定义一个三维数组,分别用于逐层传值、标记、及输出最大权值上的值。
b.从最后一层开始,两两比较出较大值传递到上一层,并标记。
c. 传递到顶层是结果即是所求最大权值,输出。
d. 按照标记,逐步输出路径上的值。
e.根据上述设计,程序主体框架如下:
int main()
{
int n;
cin>>n;
int A[n][n][3]; //定义数组
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
cin>>A[i][j][1];
A[i][j][2]=A[i][j][1]; //两个相同数组,一个用来运算一个用来输出
A[i][j][3]=0;
}
for(int i=n-1;i>=1;i--)
for(int j=1;j<=i;j++)
{
if(A[i+1][j][1]>A[i+1][j+1][1])
{
A[i][j][1]=A[i][j][1]+A[i+1][j][1];
A[i][j][3]=0;
}
else
{
A[i][j][1]=A[i][j][1]+A[i+1][j+1][1];
A[i][j][3]=1; //以1和0来判断是否通过,方便最后计算
}
} //从最后一层开始,每两个比较,更大的加到上面一节,依次判断相加,到最后一行就是最大权值
cout<
int j=1; for(int i=1;i<=n-1;i++) { cout<
j=j+A[i][j][3]; //以之前判断的是否通过得出下一各数是下一行第几个 } cout<
return 0; } 1. 在调试过程中,需要验证算法是否正确,设计以下测试数据,并观察打印的结果。 9 12 15 10 6 8 2 18 9 5 19 7 10 4 16 1. 从测试结果看,在这一组数据的运算中,能得到最大权值及其路径,且一切正常。 2. 但是当数据复杂度太大时,因为int型的容量问题,会导致无法算的结果。 3. 在这一题中,充分运用了图论中树的知识,逐步运算并标记。 给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出。 给定了整数个数,所以可以使用数组解决问题,在数组中进行统计,并根据数的个数来排序输出。 自行编写数组中的使用的排序算法及计数算法,然后进行相应的运算得出结果。设置两个数组一一对应,对每一个输入的数字进行for循环计数,因为结果中需要对一些重复的数进行删除,所以可以在计算个数时,把多于1的数字对应的计数数组中值赋值为0。 1. 主模块设计 a. 设计一个数组和原数组一样用于排序,再设计一个数组大小和原数组一样,但是其中的元素都为0,为计数数组。 b. 先对输入的数组进行计数,利用二重for循环对每一个数都计数,然后再进行一次循环,把重复的数删除(即将个数记为0)。 c. 根据个数进行排序,并且因为存在个数相同的情况,所以在排序中添加一个对数字大小的判断,把小数排在前面。 d. 输出,把对应计数数组不为0的数进行输出,输出数字+空格+个数。 for(int i=0;i for(int j=0;j { if(C[j] { int t,s; t=C[j+1]; C[j+1]=C[j]; C[j]=t; s=B[j+1]; B[j+1]=B[j]; B[j]=s; //将各个数字按照个数进行排列 } } for(int i=0;i for(int j=0;j { if(C[i]==C[j]) { if(B[i]
{ int t,s; t=C[i]; C[i]=C[j]; C[j]=t; s=B[i]; B[i]=B[j]; B[j]=s; } } } for(int i=0;i { if(C[i]!=0) cout<输出个数不为0的数 else continue; } 五、 调试与测试 1. 验证是否对于多个出现次数相同的数字,对于负数是否成立。设计以下几组数据: (1)12 5 2 3 3 1 3 4 2 5 2 3 5 (2)9 -1 2 5 7 7 8 8 -1 -9 (3)14 -2 -2 -5 -5 5 6 7 -8 1 -1 -2 0 5 8 1. 对于三组数据,程序都能正常运行得出答案,并且不会出现重复输出的问题,说明程序算法合理。 2. 可以发现,此算法是对所有出现过的数字进行计数并排序,这样会比较复杂,需要对重复的数字进行处理,但是能存储较多数据,而如果采用下标的形式,即先设置一个长数组,将题目的输入看成是这个数组的下标,然后计数就不用处理重复数字了,但是这样无论是算哪一组数据都需要使用一个长数组,对于一些简单的问题可能会使问题更复杂。 在我们现在使用的日历中, 闰年被定义为能被4整除的年份,但是能被100整除而不能被400整除的年是例外,它们不是闰年。例如:1700, 1800, 1900 和 2100 不是闰年,而 1600, 2000 和 2400是闰年。 给定从公元2000年1月1日开始逝去的天数,你的任务是给出这一天是哪年哪月哪日星期几。 将这个题目看成一个数数的问题,依次数出年份,月份,日期,星期几。并且需要考虑到一些特殊的年份和月份,对于这些特殊的数据需要优先判断和处理,防止出错。 1. 根据闰年的定义,设置一个函数判断某一年是否是闰年,再对闰年进行处理,如果是闰年,那么第二个月的天数就会发生相应的变化。 2. 计算星期几的时候,因为是从2000年1月1日开始的,而这一天又刚好是周一,所以我们可以对过去的天数除7取余数,然后根据余数判断出是一周中的第几天,也就是星期几。 1. 主模块设计 a. 输入时,先定义一个数组存储数据,对于每一个输入进行判断,如果是-1则停止输入,如果不是则将这个数据存储子在数组中并进行下一次输入。 b. 使用for循环调用存储数组中的数据运算,代入cal(A[i], year, month, day)函数中,然后将运算得到的年、月、日、星期几输出。 c. 在输出时,因为要使输出的数字为01格式,所以要对不满足的数字进行填充,使用 int main() { int year,month,day; int A[100]; int s=0; for(int i=0;i<100;i++) { cin>>A[i]; if(A[i]==-1) break; else s+=1; } for(int i=0;i { cal(A[i], year, month, day); cout << year << "-" < week(A[i]); cout << endl; } cal(A[s-1], year, month, day); cout << year << "-" < week(A[s-1]); return 0; } 2. yearDays(int year) 函数 对于每一个代入这个函数的年份进行判断,判断是否是闰年。 3. (monthDays(int year, int month) 判断月份函数,先将在里面调用yearDays()判断某一年有多少天,然后不足一天总天数时就判断是在哪一个月。 4. cal(int n, int &year, int &month, int &day) 在这个函数中,可以调用上述所以函数,包括对星期几的判断,都是在这个函数中,因为主函数中所有的数据都是通过这一个函数传递到这些函数中进行运算的,而我们需要的结果也是这个函数通过对其他函数的调用而算出的。 1. 验证这个算法在涉及一些特殊的年份和月份时是否会出问题。 设计以下几组数据: 1 1730 365 366 1750 -1 1. 特殊的数据,如1,365和366,程序可以正确打印结果,并不会出现缺一天或者多一天的情况,而对于1730和1750,可以算出在这段时间内是有闰年的,但是也没有产生错误。 2. 另外,在运算中,如果数据太多,可能数组没法存储,此时我们考虑使用向量来进行存储数据,可以确保多组数据都能正常存储和运算。 约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1 开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。 这是一个约瑟夫环的问题,主要是需要注意环状数据的运算,需要对数据进行重复的的运算,直到最后得到题目要求的答案。而在运算中,每一个数字每一次运算所对应的编号也是不同的,所以我们可以设计一个算法,让这些猴子的编号在运算中始终不变,并且也能做到对于一些数据的剔除,最后剩余一个数据时,即为所求猴子编号。 1. 每次数到b时都要剔除一只猴子,那么我们可以使用一个数组,长度等于猴子的个数并与其一一对应,初始值都赋为1,然后在后面的运算中,如果要剔除,则将对于那只要剔除猴子的数组元素标记为0。 2. 而因为要进行数数,所以可以设置两个同时变化的数,一个专门进行数数,另一个用于求出这个数所对应的猴子是哪一个,当第一个数变化到可以整除b时,则将对应的猴子标记为0。 1. 主模块设计 a. 输入:使用while,对于每一次输入都判断,如果不是 0 0 ,则进入while循环中运算,并在其中输出,若为0 0 ,则结束程序。 b. 定义i 和 w ,同时变化,但是i 不能超过猴子的最大编号,w每次加1可以加到运算结束,大小无限制。 c. i 在变化时,超过猴子最大编号则减小至第一位开始,并且对于i 所在的每一位进行判断,看这个猴子是否被已经被剔除,如果已经被剔除(即标记了0),则i再加1,而w 不变。 while(1) { if(i>n) i=i-n; while(A[i]==0) { i+=1; if(i>n) i=i-n; } if(w%b==0) { A[i]-=1; s+=1; } i+=1; w+=1; if(s==a-1) { for(int j=1;j<=a;j++) { if(A[j]==1) cout< } ss+=1; } if(ss!=0) break; } 1. 考虑到这个程序中定义的i 和 w 能否在渐变中准确无误。 设计以下几组数据: 6 2 12 4 8 14 1 2 0 0 1. 由打印的结果看,这个算法是没有问题的,对于8 14 和 1 2 这样的数据也能正确处理。 2. 在编写程序中,可以发现一个约瑟夫环的计算公式,如果利用这个公式计算,这个问题会变得十分简单,推导过程如下: /* If number = 3 * f(1) = 0 * f(2) = 1 = (f(1) + 3) % 2 * f(3) = 1 = (f(2) + 3) % 3 * f(4) = 0 = (f(3) + 3) % 4 * f(5) = 3 = (f(4) + 3) % 5 * ... * f(n) = x = (f(n-1) + 3) % n * */ 即:我们可以利用f(n) = x = (f(n-1) + 3) % n这一公式,通过for循环算出最后剩余那一只猴子的编号。 一个多项式可以表示为一组数对,数对中第一个数始终为整数,且唯一,表示多项式的次数,另一数表示为对应的系数且不为0。输入两组数对,每组以0 0作为结束,实现对两个多项式的加法并按降幂输出结果数对 这里并没有给出数的个数,所以我们不能使用数组来存储数据,应该用其他方式。 题目意思类似于数学中的多项式展开式,这是对不同次幂的系数的一个统计,相当于合并同类式。 1. 使用向量存储数据,当输入为0 0时不存储,并且计数,当计入的0 0有两组时,开始进入程序进行运算。 2. 计算和输出使用另一个向量,避免重复,只把相同系数中的一个计入这个向量中,最后输出。 1. 主模块设计 a. 使用while进行输入,方便输入时对0 0的判断; b. 遍历计数,对于每一个数都进行计数,然后将重复的数中其中一组放入输出向量中。 1. 需要考虑: 系数为0的数据; 多次重复出现的数据; 在排序中容易出现的问题。 设计数据: 0 12 3 8 1 2 15 5 0 10 0 0 3 12 30 1 15 5 0 0 1. 对于不确定个数的一组数据,可以采用向量来存储,如果是用数组则容易溢出或者浪费内存; 2. 在处理一些重复数据时,可以采用清零的方式将其剔除,防止对运算产生影响或者重复输出。 在选美大奖赛的半决赛现场,有n名选手(2 选手数量: 7 选手得分: 5,3,4,7,3,5,6 宣布名次: 3,5,4,1,5,3,2 请编程帮助大奖赛组委会完成半决赛的评分排名工作。 这是一个简单的排序问题,但是在排序的过程中我们还需要把相同的数字赋予相同的次序,并实现其他数的次序变化。 设置一个数组和原数组一一对应,然后使用for循环查找出所有数中比某一个数大的数的个数,然后再考虑次序相同的情况,对其进行删减,得到依次递减的次序数。 1. 主模块设计 a. 设置一个数组和原数组一一对应,然后使用for循环查找出所有数中比某一个数大的数的个数,则先定义这个个数就是这个数的排名。 b. 对于存储次序的数组进行遍历,查看是否有重复的次序,如果有,则这个数的下面所有次数的次数都减去重复的次数。 c. 输出经过改变得存储次序的数组。 1. 当某一位或者某几位上的数有多个时,这个程序可能会有问题,所以设计如下数据: (1)8 1 5 6 6 4 5 1 1 (2)16 1 1 9 8 9 9 8 8 8 8 7 7 1 1 2 2 1. 打印结果无误,说明程序没有问题。 2. 这个题目的重点就是如何处理多个相同的数字,在这个程序中采用的是位数加减的方式来算,还可以直接求出比自己小的数的个数,然后再和数字的个数总和相互比较得出答案。 如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易。”现在,给定哈利应付的价钱P和他实付的钱A,你的任务是写一个程序来计算他应该被找的零钱。 钱币的单位类似于进制的转化,我们可以将输入的数全部转化为十进制,也就是把这个数字算成有多少个基本单位。 将输入的数转化为有以基础单位(即纳特)为单位的数字,然后进行加减运算,最后再按照魔法世界的货币系统来转化所得的得数,最后输出。 1. 主模块设计 a. 输入时,因为题目给出的数是中间有一个小数点的,所以我们可以设置六个数,然后在两个数输入中间插入一个cin.get()==“.”,由此来消除小数点的影响,也可以采用C语言的输入方式,使用scanf输入。 b. 设置两个数分别存储需要的钱币数和能支付的钱币数,然后相减计算出差值,最后将差值转为加隆-纳特-西可的形式输出。 输入样例: 10.16.27 14.1.28 计算结果:3.2.1 (正确) 1. 这个题目主要要考虑这个货币系统中不同单位的转化和在输入输出时对小数点的处理,可以采用C语言中的scanf和sprintf 俩输入输出,确保小数点不会有影响。 2. 在计算中,要考虑到是三个位数的数字相加等于原数值,所以要考虑到我们的换位是对算完某一位后剩下的数字来计算。 以1为中心,用2,3,4, ..., n, ..., n*n的数字围绕着中心输出数圈, 如若n=4,则 7 8 9 10 6 1 2 11 5 4 3 12 16 15 14 13 数字绕圈由1开始到最外围,一直到数字变为n*n。这种类似于画图的实现,可以采用模拟的方法,当然由于这个是一些特殊的数,所以如果能找到某种规律,是可以按照规律直接输出的。 1. 在模拟绕圈的过程中,需要考虑到绕圈时在什么时候开始向另一个方向拐,可以设置一个n+2*n+2的数组,即相当于在题目要求的数圈上再加一圈,然后把外面的一圈全部赋值为1,中间的赋值为0,则在模拟的每一步中,都对下一步进行判断,如果下一步所要到达的那一块不是0,则换一个方向进行绕圈。 2. 对于初始点的考虑,我们从奇数偶数两种情况进行分析,可以看出奇数是从右上角开始,偶数是从左下角开始。 3. 在绕圈的过程中,一共有四种方向,向上、向左、向右、向下,我们对于四种情况都构造一个函数进行运算模拟数圈的产生。并设置一个数进行判断在某一段时间是进行哪一个方向的绕圈。 1. 主模块设计 a. 先定义一个n+2*n+2的数组,然后进行不同区域不同的赋值。 b. 设置一个act包含四种操作,向下,向上,向左和向右,并且在运算中通过对act的改变和判读来实现不同的数字赋值。 需要考虑对输入数字的奇偶,设计数据看打印情况,看程序对于奇数偶数不同的算法能否区分: (1) 5 (2) 6 (3) 12 (4) 17 1. 打印结果证明,该程序无误,对于奇偶性不同的输入值都能进行正确的运算并输出结果。 2. 在这种图形的题目中,最简单并且最不容易犯错的方法就是模拟了,但是也有可能会有些方面没有考虑完全,从而导致错误。另外一种方法是找出规律,但是图形类题目有很多并没有相应的太明确的规律,找出来需要耗费的时间太长,难度太大。 进制转化类题目,将其转化为十进制的数字,然后进行运算。 1. 对于输入的一串字符串,根据每一位和“a”的差值来计算这一位中的数字是多少,比如a,转为十进制则为0,z则为25。 2. 把每一位转化为相应的十进制数之后,根据它所处的位数乘以相应的10的次方,然后相加得到了这个数字用十进制表示的形式,然后进行运算。 1. 主模块设计 a. 将输入的数字定义为string型字符串,然后在运算中让字符串中每一位和a进行比较,并乘以相应的十的次方然后存储。 b. 将两个数字经过转化后相加,在使用stringstream将相加的得数转化为string型字符串,再对这一串字符串中每一位进行运算,求出在这一位中应该是哪一个字母,最后重组字符串,输出。 int main() { int n; cin>>n; vector string A[n][2]; char B[26]; for(int i=0;i<26;i++) B[i]='a'+i; for(int i=0;i for(int j=0;j<2;j++) { cin>>A[i][j]; a[i].push_back(A[i][j]); } 五、 调试与测试
六、 分析与总结
题2:数字排序 (ID: 1002 )
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
六、 分析与总结
题3:日历问题 (ID: 1006 )
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
五、 调试与测试
六、 分析与总结
题4:约瑟夫问题 (ID: 1008 )
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
五、 调试与测试
六、 分析与总结
题5:多项式加法 (ID: 1011)
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
for(int i=0;i
for(int j=0;j
{
if(a[j]
{
int temp1,temp2;
temp1=a[j];
a[j]=a[j+1];
a[j+1]=temp1;
temp2=b[j];
b[j]=b[j+1];
b[j+1]=temp2;
}
}
for(int i=0;i
for(int j=i+1;j
{
if(a[i]==a[j])
{
b[i]+=b[j];
b[j]=0;
}
}
五、 调试与测试
六、 分析与总结
题6:选美比赛 (ID: 1014)
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
for(int i=0;i
for(int j=i+1;j
{
if(B[i]==B[j])
{
for(int k=0;k
{
if(B[k]>B[i])
C[k]-=1;
}
C[i]-=1;
C[j]-=1;
}
}
五、 调试与测试
六、 分析与总结
题7:在霍格沃茨找零钱 (ID: 1028)
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
m = a1 * 17 * 29 + a2 * 29 + a3;
n = b1 * 17 * 29 + b2 * 29 + b3;
s = m-n;
if (s > 0)
{
cout << '-';
}
s = abs(s);
c1 = s / (17 * 29);
c2 = (s - c1* 17 * 29) / 29;
c3 = s - c1 * 17 * 29 - c2 * 29;
cout<
五、 调试与测试
六、 分析与总结
题8:数圈 (ID: 1044)
一、 问题描述
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)
while(true) {
table[x][y]=fill;
fill--;
if(fill==0) break;
if(act==Left) {
if(table[x-1][y]!=0) {
act=Down;
y--; }
else
x--; }
else if(act==Right) {
if(table[x+1][y]!=0) {
act=Up;
y++;
}
else
x++;
}
else if(act==Up) {
if(table[x][y+1]!=0) {
act=Left;
x--;
}
else
y++;
}
else if(act=Down) {
if(table[x][y-1]!=0) {
act=Right;
x++;
}
else
y--;
}
}
五、 调试与测试
六、 分析与总结
题9:小A的计算器 (ID: 1047)
一、 问题描述
以往的操作系统内部的数据表示都是二进制方式,小A新写了一个操作系统,系统内部的数据表示为26进制,其中0-25分别由a-z表示。
现在小A要在这个操作系统上实现一个计算器,这个计算器要能实现26进制数的 加法运算。你能帮小A实现这个计算器吗?
二、 问题分析
三、 算法分析
四、 详细设计(从算法到程序)