在解决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");
}