CCF-训练50题-NO.1-数塔问题
2017-11-09 23:10
246 查看
问题描述
给定一个数塔,如下图所示。在此数塔中,从顶部出发,在每一结点可以选择走左下或右下,一直走到底层。请找出一条路径,使路径上的数值和最大。问题分析
求最大路径最简单的思维是:由于每个结点都有两个方向,于是最后有有限组方案。只要找出最大的方案即可。但是这样的方法过于复杂遇上较大的数据将会有可能陷入run-time error。但是如果我们每层每层看将会大大减轻工作量。第n-1行(n为最后一行)的每个数据计算为该数据加上他的两个子结点的后的最大值,这时其值表示的是从他这点出发可以走的最远路程。n-2行到第一行以此类推。因此,从最后一行一直走到第一行的时候,第一行断的唯一结点表示的就是从该点出发的最远路程。最后求路径则是从第一行走回来,如果该结点的值减去初始值是等于其左边(或右边)的子结点的值,则表明路径的下一步是其左边(或右边)的子结点。
算法分析
采取先找最大生成树的方式,在查找遍历路径。最大生成树的查找方式采取动态规划的方式既涉及动态规划,就有其自己的状态方程d[i][j]=a[i][j]+max{d[i+1][j+1],d[i+1][j]}
状态方程中a[m]
是指第m行第n个元素;d[m]
是指在第m行第n个元素这里到最底层的最大路径。每个结点的最大路径保存的是其子结点中有较大大路径的路径值加上其本身元素。通过这样的动态规划可以直接算出最大路径,而因为途中的最大路径的覆盖,得用倒推法算出路径。
代码:
#include <iostream> using namespace std; int main(){ int n; cin>>n; int *a=new int[n*n]; int *d=new int[n*n]; int *line=new int ; for (int i=0;i<n;i++){ for (int j=0;j<i+1;j++){ cin>>a[i*n+j]; } } line[0]=a[0*n+0]; for (int j=0;j<n;j++) { d[(n-1)*n+j]=a[(n-1)*n+j]; } for (int i=n-2;i>=0;i--){ for (int j=0;j<=i;j++){ if (d[(i+1)*n+j]<d[(i+1)*n+(j 4000 +1)]) { d[i*n+j]=a[i*n+j]+d[(i+1)*n+(j+1)]; } else { d[i*n+j]=a[i*n+j]+d[(i+1)*n+j]; } } } int pos=0; for (int i=0;i<n;i++){ if (d[(i+1)*n+pos]==(d[i*n+pos]-a[i*n+pos])){ line[i]=a[i*n+pos]; } else { line[i]=a[i*n+(pos)]; pos++; } } cout<<d[0*n+0]<<endl; for (int i=0;i<n;i++) cout<<line[i]<<" "; return 0; }
经验之谈:
这题目的核心在于将状态方程写出来,状态方程的核心就在于采用覆盖计算的方法先计算出最大的路径,再用逆向思维遍历出最大路径的各个结点。对于这样的题目要求我们对算法的状态方程有着很熟的把握。以及有着较好的顺逆向思维。因此,要多做这样的题目,这样才能对着代码有着更好的把握。相关文章推荐
- CCF-训练50题-NO.3-数字排序问题
- CCF-训练50题-NO.4-相邻数对问题
- CCF-训练50题-NO.6-字符串匹配问题
- CCF-训练50题-NO.7-日历问题
- CCF-训练50题-NO.9-约瑟夫问题
- CCF-训练50题-NO.16-字符串数字置换
- CCF-训练50题-NO.23-锤子剪刀布
- CCF-训练50题-NO.27-挖掘机技术哪家强
- CCF-训练50题-NO.10-恺撒Caesar密码
- CCF-训练50题-NO.17-写出来吧
- CCF-训练50题-NO.24-个位数统计
- CCF-训练50题-NO.28-到底买不买
- CCF-训练50题-NO.18-成绩大排队
- CCF-训练50题-NO.19-说反话
- CCF-训练50题-NO.20-A+B和C比大小
- CCF-训练50题-NO.5-画图
- CCF-训练50题-NO.29-最少钱币数
- CCF-训练50题-NO.30-蛇形矩阵
- CCF-训练50题-NO.25-组个最小数
- CCF-训练50题-NO.26-在霍格沃茨找零钱