hdu 2084 数塔

来源:互联网 发布:php清除cookie 编辑:程序博客网 时间:2024/06/05 00:39

题目:点击打开链接

题目大意:数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

分析:经典的动态规划问题,也是做的第一道动态规划问题,因为初学,在刘汝佳的书上也有详细讲解,在这里好好分析一下吧。

因为是从顶层走到底层,没经过一个位置所走的数字和就不一样,我们用d[I][j]来存储这个位置的状态,即总共走的路程,从上到下,确定的一点就是顶点,所以这个问题就等价于求d[1][1],从(i,j)点出发后只能向下或右下走,要求经过和最大,所以要走d[I+1][j]和d[I+1][j+1]大的那个,所有有状态转移方程d[I][j]=a[I][j]+max(d[I+1][j],d[I+1][j+1]),其中a数组存储的是(i,j)这点的值。

状态方程有了之后就好办了,我们可以有三种计算方法。

法一:递归计算:

int solve(int i,int j){    return a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));需要注意边界,当I==n时,I+1,j+1会越界}

这样做时间效率很低,因为会重复计算一些状态(可能做了一半的无用功)

为了避免访问过的状态不会重新访问,我们需要把状态访问结果保存在d数组中,在递归的时候判断是否已经放问过,这种方法称为记忆化搜索

法二:记忆化搜索:数组d需要初始化memset(d,-1,sizeof(d))fa

int solve(int i,int j){    if(d[i][j]>=0)return d[i][j];    return d[i][j]=a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));}


法三:递推计算:

因为需要求出d[1][1],所以我们从底层逆推,状态转移方程d[I][j]=a[I][j]+max(d[I+1][j],d[I+1][j+1]),我们要初始化边界d[n]

for(int j=1;j<=n;j++)d[n][j]=a[n][j];        for(int i=n-1;i>=1;i--)            for(int j=1;j<=i;j++)                d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);

代码:

#include<iostream>#include<string.h>#include<algorithm>using namespace std;int T,n,a[105][105],d[105][105];int main(){    cin>>T;    while(T--){        cin>>n;        memset(d,0,sizeof(d));        for(int i=1;i<=n;i++){            for(int j=1;j<=i;j++)                cin>>a[i][j];        }        for(int i=n;i>=1;i--){  //也可以直接从n层开始因为d数组已经初始化0,d[n+1]层都是0,不影响            for(int j=1;j<=i;j++)                d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);        }        cout<<d[1][1]<<endl;    }    return 0;}


0 0
原创粉丝点击