C++编程入门十题
题1:数塔 (ID: 1000 )
一、 问题描述
给定一个数塔,如下图所示。在此数塔中,从顶部出发,在每一节点可以选择走左下或右下,一直走到底层。请找出一条路径,使路径上的数值和最大。
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<<A[1][1][1]<<endl;
int j=1;
for(int i=1;i<=n-1;i++)
{
cout<<A[i][j][2]<<" ";
j=j+A[i][j][3]; //以之前判断的是否通过得出下一各数是下一行第几个
}
cout<<A[n][j][2];
return 0;
}
五、 调试与测试
1. 在调试过程中,需要验证算法是否正确,设计以下测试数据,并观察打印的结果。
9
12 15
10 6 8
2 18 9 5
19 7 10 4 16
六、 分析与总结
1. 从测试结果看,在这一组数据的运算中,能得到最大权值及其路径,且一切正常。
2. 但是当数据复杂度太大时,因为int型的容量问题,会导致无法算的结果。
3. 在这一题中,充分运用了图论中树的知识,逐步运算并标记。
题2:数字排序 (ID: 1002 )
一、 问题描述
给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出。
二、 问题分析
给定了整数个数,所以可以使用数组解决问题,在数组中进行统计,并根据数的个数来排序输出。
三、 算法分析
自行编写数组中的使用的排序算法及计数算法,然后进行相应的运算得出结果。设置两个数组一一对应,对每一个输入的数字进行for循环计数,因为结果中需要对一些重复的数进行删除,所以可以在计算个数时,把多于1的数字对应的计数数组中值赋值为0。
四、 详细设计(从算法到程序)
1. 主模块设计
a. 设计一个数组和原数组一样用于排序,再设计一个数组大小和原数组一样,但是其中的元素都为0,为计数数组。
b. 先对输入的数组进行计数,利用二重for循环对每一个数都计数,然后再进行一次循环,把重复的数删除(即将个数记为0)。
c. 根据个数进行排序,并且因为存在个数相同的情况,所以在排序中添加一个对数字大小的判断,把小数排在前面。
d. 输出,把对应计数数组不为0的数进行输出,输出数字+空格+个数。
for(int i=0;i<n;i++)
for(int j=0;j<n-1;j++)
{
if(C[j]<C[j+1])
{
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<n;i++)
for(int j=0;j<n-1;j++)
{
if(C[i]==C[j])
{
if(B[i]<B[j])
{
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<n;i++)
{
if(C[i]!=0)
cout<<B[i]<<" "<<C[i]<<endl; //输出个数不为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. 可以发现,此算法是对所有出现过的数字进行计数并排序,这样会比较复杂,需要对重复的数字进行处理,但是能存储较多数据,而如果采用下标的形式,即先设置一个长数组,将题目的输入看成是这个数组的下标,然后计数就不用处理重复数字了,但是这样无论是算哪一组数据都需要使用一个长数组,对于一些简单的问题可能会使问题更复杂。