【转】数字三角形-递推-动态规划

转载自:http://hi.baidu.com/hero1987116/item/bf9d6f5c5cb7ae3095eb050b

(转)数字三角形-递推-动态规划

数字三角形-递推-动态规划Algorithmic Article2008-4-6

在解决01背包问题后,我们来总结一下动态规划的方法。
首先来说,递推一是种必备的能力。看数字三角形的题目:
[例] 如下所示为一个数字三角形:
  7
  3 8
  8 1 0
  2 7 4 4
  4 5 2 6 5
请编一个程序计算从顶至底的某一条路径,使该路径所经过的数字的总和最大。
要求●每一步可沿直线向下或右斜线向下走;
  ●1<=三角形行数<=100;
  ●三角形中的数字为整数0,1,…,99。
  输入:输入文件第一行为一个自然数,表示数字三角形的行数n(1<=n<=100),
接下来的n行表示一个数字三角形。
  输出:输出文件仅有一行包含一个整数(表示要求的最大总和)。

首先我们看看用递归的方式来做:

#include<stdio.h>
long int max=0,sum=0;
int n,a[31][31];
void search(int x,int y,int sum)
{
     if(x==n-1)
               {
                 if(sum>max) max=sum;
                 return;
               }
            
      search(x+1,y+1,sum+a[x+1][y+1]);            
      search(x+1,y,sum+a[x+1][y]);
      /*在这里x做记录层数的功能*/
}
int main()
{
    int i,j,dep;
    FILE *fp=fopen("in.txt","r");
    FILE *fq=fopen("out.txt","w");
    fscanf(fp,"%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<=i;j++)
           fscanf(fp,"%d",&a[i][j]);
    search(0,0,a[0][0]);
    fprintf(fq,"%d",max);
}

递归的方法简单明了。对每条可走的路径进行计算,把最大的结果保存在max中。
对递归还不清楚的同学注意体会递归的过程:
以层数为递归边界,这样问题可以简化成在x,y坐标轴上的操作。如果只有2层,则相加的结果就是
[x][y]+[x+1][y]或者[x][y]+[x+1][y+1]。如果是3层呢?自己做一下。
递归的时间复杂度很高,这里为2的n次方。当层数到30左右的时候,我的Lenovo P4 3.2G的机器居然
要6秒钟。很显然要把运算时间控制在1秒,我们得找其他办法。

进行递推如图:

我们来进行按三角形的行划分阶段,用另外一个二维数组f[i][j]进行推理,若行数为 n,则可把问题看做一个n-1个阶段的决策问题。先求出第n-1阶段(第n-1行上各点)到第n行的的最大和,再依次求出第n-2阶段、第n-3阶段……第1阶段(起始点)各决策点至第n行的最佳路径。
设:f[i,j]为从第i阶段中的点j至第n行的最大的数字和;
则: f[n,j]=a[n,j]   0<=j<n
    f[i,j]=max{a[i,j]+f[i+1,j],a[i,j]+f[i+1,j+1]} 0<=j<=i.
    f[0,0]即为所求。

说简单点:从最下面一层开始,从左到右每两个相比较,大的加到他们上面一层去,再进行比较。大的再和上面的相加,这样保证每次都是最优解。

这回我试验了90层的三角形时间<3ms!

程序如下:

#include<stdio.h>
int n,a[30][30];
int main()
{
    int i,j,f[30][30]={0};
    FILE *fp=fopen("in.txt","r");
    FILE *fq=fopen("out.txt","w");
    fscanf(fp,"%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<=i;j++)
           {
           fscanf(fp,"%d",&a[i][j]);
           if(i==n-1)f[i][j]=a[i][j]; /*把a[i][j]最底下一排赋给f[i][j]*/
           }
    for(i=n-2;i>=0;i--) /*从倒数第二排开始向上逆推*/
       for(j=0;j<=i;j++)
             {
             if(f[i+1][j]>f[i+1][j+1])
                         f[i][j]=a[i][j]+f[i+1][j];
                       else f[i][j]=a[i][j]+f[i+1][j+1];
              }
    printf("%d,%d",i,j);   
    printf(" %d",f[0][0]);
    system("pause");
}

以上是逆推。那么从三角形上面顺推呢?看图:

程序如下:

#include<stdio.h>
int n,max,a[30][30];
int main()
{
    int i,j,f[30][30]={0};
    FILE *fp=fopen("in.txt","r");
          fscanf(fp,"%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<=i;j++)
           fscanf(fp,"%d",&a[i][j]);
    f[0][0]=a[0][0];
    for(i=1;i<n;i++)
        {
        f[i][0]=a[i][0]+f[i-1][0];
        for(j=1;j<=i;j++)
           if(f[i-1][j-1]>f[i-1][j])
                      f[i][j]=a[i][j]+f[i-1][j-1];
           else        f[i][j]=a[i][j]+f[i-1][j];
         }
     for(j=0;j<n;j++)
           printf("%d ",f[n-1][j]);
    for(j=0;j<n;j++)
        if(f[n-1][j]>max)max=f[n-1][j];
           printf("%d ",max);
   
    system("pause");
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值