上一次我介绍了动态规划的基本思想,并且用算法导论里最简单的动态规划例子——钢条切割来加以辅助介绍,今天我会按照上次的步骤来,用另一个简单的DP例子“数塔问题”来演示动态规划的另一种最常用的实现方式——带备忘的自顶向下法(丫的就是递归-_-)。
数塔问题呢,就是给你若干行(这里用n表示塔的层数)数字组成的塔,且第k层塔有k个数字,从最高点开始在底部任意处结束的路径经过数字的和的最大。每一步可以走到左下方的点也可以到达右下方的点。
数据:
n = 5;
7
3 8
8 1 0
2 7 4 4
4 5 2 6 1
根据动态规划的思想,我们可以先根据上次我介绍的从底下往上推的方式,如果想让路径值最大,那么从最后一层走到倒数第二层一定是倒数第二层的那个数与它左下or右下的数的和是最大的,比如对于倒数第二层第一个数2,走max(4->2,5->2),第二个数7,可以选择max(5->7,2->7),以此类推......直到第二层,在它之下选择的路径一定是5->7->8->3,最后走第一层只有那一个数字7,所以结果就是5+7+8+3+7=30.(如果可以,大家可以先用递推来写一下我刚才所描述的,然后自己想想如何将递推转化为递归。
/********************************************************************/
自己想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想不出来再往下看。。。。。。
/********************************************************************/
2014.3.27
By:haoran
数塔问题呢,就是给你若干行(这里用n表示塔的层数)数字组成的塔,且第k层塔有k个数字,从最高点开始在底部任意处结束的路径经过数字的和的最大。每一步可以走到左下方的点也可以到达右下方的点。
数据:
n = 5;
7
3 8
8 1 0
2 7 4 4
4 5 2 6 1
根据动态规划的思想,我们可以先根据上次我介绍的从底下往上推的方式,如果想让路径值最大,那么从最后一层走到倒数第二层一定是倒数第二层的那个数与它左下or右下的数的和是最大的,比如对于倒数第二层第一个数2,走max(4->2,5->2),第二个数7,可以选择max(5->7,2->7),以此类推......直到第二层,在它之下选择的路径一定是5->7->8->3,最后走第一层只有那一个数字7,所以结果就是5+7+8+3+7=30.(如果可以,大家可以先用递推来写一下我刚才所描述的,然后自己想想如何将递推转化为递归。
/********************************************************************/
自己想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想想
想想想想想想不出来再往下看。。。。。。
/********************************************************************/
好的,不淘气了,先简单说一下递归与递推的关系,递归与递推可以相互转化,一个是隐性的循环,另一个是显性的循环,理论上递归问题都可以转换为循环来处理,比如我们想知道3的阶乘,那么我们就要先知道2的阶乘,想知道2的阶乘就要先知道1的阶乘,那么我们给出1的阶乘为1,ok,那么我们也就能推回2 的阶乘为1*2,最终我们也就知道3的阶乘为3*2*1了。这要是递推就没那么多事了,直接从1乘到3不就完事了嘛,
对于这道题,我还是像上次一样,写一个返回两个数最大值的themax函数,用来返回这一层走到下一层的最优路径。因为递归和递推差了一个分治的“分”的过程,等到触底的时候再往上折返,这一过程恰好与递推是反着来的。
我用数组a来储存数塔,用数组b作为动态规划过程中的记录表格,根据每一次可以往这个数的左下or右下走,可以得到状态转移方程b[i][j] = a[i][j] + themax(f(i+1,j),f(i+1,j+1));
在程序运行之前先把数组b初始化为-1,递归的时候,如果发现某一层的值已经不为-1了,即>=0时,就返回该值,并将该值储存在b数组相应的位置上,否则的话进行下一层的递归处理。
int f1(int i,int j)
{
if(b[i][j]>=0)
return b[i][j]; //返回已经解决的局部最优解
return b[i][j] = a[i][j] + ( i == n ? 0 : themax(f1(i+1,j),f1(i+1,j+1) ));//
//否则的话继续往下找
}
下面是我的源代码:
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int a[100][100]={0},b[100][100];
int themax(int a,int b)
{
return a > b ? a : b ;
}
int f1(int i,int j)
{
for(int ii=0;ii<=n;ii++)
{
for(int k=n;k>=ii;k--)printf(" ");
for(int jj = 0 ; jj <= ii ; jj++ )
printf(" %d ",b[ii][jj]);
cout<<endl;
}
if(b[i][j]>=0)
{
printf("b[%d][%d] = %d\n",i,j,b[i][j]);
return b[i][j];
} // int x = f1(i+1,j),y=f1(i+1,j+1);
printf("b[%d][%d] = a[%d][%d] + (%d == %d ? 0 : themax(f1(%d,%d),f1(%d,%d))\n"
,i,j,i,j,i,n,i+1,j,i+1,j+1);
//printf("%d = %d + ( i = %d %d×ó %dÓÒ)\n",b[i][j],a[i][j],i,x,y);
return b[i][j] = a[i][j] + ( i == n ? 0 : themax(f1(i+1,j),f1(i+1,j+1) ));
}
int main()
{
freopen("in.txt","r",stdin);
int i,j,p,sum=0;
memset(b,-1,sizeof(b));
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin>>a[i][j];
for(i=0;i<=n;i++)
{
for(int k=n;k>i;k--)printf(" ");
for( j = 0 ; j <= i ; j++ )
printf(" %d ",a[i][j]);
cout<<endl;
}
printf("%d\n",f1(0,0));
}
return 0;
}
END
2014.3.27
By:haoran