源于对算法竞赛一书的理解
最优子结构:先来看一个数塔
从最顶部的结点出发,每次可以向左或者向右走一格,走到最底部时把沿途经历的结点全部加起来,如何走才能和最大。
可以用一个二维数组a(i,j)记录这张数塔图,每一个结点的位置(i,j),都可以看作是一个状态,则从当前状态出发,下一个状态可以是(i+1,j)或者是(i+1,j+1),而题目就是求从状态(0,0)出发,不断的状态进行转移,转移到最底部时所得的和最大值。设d(0,0)为从状态(0,0)出发得到的和最大值,则d(0,0)=a(0,0)+d(1,0)或者d(0,0)=a(0,0)+d(1,1);要得到最大值只能是其中一种走法,所以d(0,0)=a(0,0)+max(d(1,0),d(1,1)),即当前最优解等于下一子状态的最优解加上当前结点值。当下一子状态都不是最优解时,当前状态一定不是最优解。这就是最优子结构。
递归算法如下:
但是这样做这道题,相当于从节点开始往下搜索,由于总共层数有n层,一共有2的n次-1个结点要被执行,所以 时间复杂度为o(2^n),过于复杂,这是因为有很多结点被重复执行了,比如从状态(2,1)要转移到(3,2),而从状态(2,2)也要转移到(3,2),这样从(3,2)这个结点往下的子树就被执行了两次,更何况,别的结点及以下的子树也会发生这种情况,时间复杂度就这样上来了。因此采用记忆化搜索来保证每个结点及以下的子树只走一次。
int solve(int i,int j)
{
return d[i][j]=a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1));
}
记忆化搜索:
如果把每从一个状态出发后所得的最优解记录在一个数组dp[i][j]中,那么当下一次又执行到这个状态时,就可以返回了,避免了重复操作。
代码int solve(int i,int j)
{
if(dp[i][j]>0)
return dp[i][j];
else
return dp[i][j]=a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1));
}
这里赋值语句本身有返回值。即把值赋给dp[i][j]后,又可以把dp[i][j]的值返回。
如果博文有什么误笔,错误欢迎留言指出。