感觉这道题出得不错,而且网上题解讲得非常好,所以~~~~~~~~~~
使用动态规划算法的关键,在于状态的定义和找到动态转移方程。
很多同学(包括我)在内最初考虑该算法的时候一定是在想:使用DP[i][j]来保存[0][0]到[i][j]的来回最大值。不过很明显,这种状态的保存只保存得了一条路径,而如果DP两次,图的状态怎么确定又是很大的问题。所以,不能这样保存状态。
既然要走两次,而题目规定了往下走只有右、下两个方向,往上走只有左、上两个方向,所以走的步数来回肯定一样的。
因此,我们不妨让两条线路同时进行,即用四维数组DP[i][j][m][n]来保存状态。
而又因为同一条路线可以是往下走的,也可以是往回走的(这只是一个相对的问题),所以可以直接假设两条线路都是从上往下走(怎么走无所谓,该线路的值是一定的)。
接下来解释一下数组的含义:DP[i][j][m][n]中事实上相当于保存了DP[i][j]和DP[m][n]之和,它的意思是第一条路线走到(i,j)处的时候第二条路线走到(m,n)处时所经过地点的好心值之和的最大值。
为了确保两条线路不会重复,我们人为规定 m > i+1 ,依旧使用四重for循环来进行状态转移。
我们的四重循环是这样的:
//用x、y来分别作为行、列
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
for(int m=i+1;m<x;m++)
for(int n=j-1;n>=0;n--)
开四重循环的话,这次题目要求比较低,只有50*50,如果大点呢?四重循环100^4不就超时了吗?。
所以,我们就考虑一下将状态压缩。
我们已经注意到,两条路线在同一时刻走的路程是相同的。
因此不难发现:
i+j=m+n。
n=i+j-m。
如此一来我们便可以省去 n 这一维数组,只剩下压缩之后的三位数组。
这个的正确性也不难证明,m+n中,m一定,n也一定是确定下来了的。
新的方程就不难列出来了:
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
for(int m=i+1;m<i+j;m++)//想想为什么是i+j
{
状态转移方程
}
注意:状态的转移中会出现一些边界情况,如 i=0 和 j=0,需要特殊判断。
核心代码:
memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++)//不断向下走
for(int j=0;j<n;j++)//向右走
for(int k=i+1;k<=i+j;k++)//向右或者向下走,所以k>i+1
{
if(k==i+1)//两条线在靠近的两行,以下部分可画图验证。
{
if(i==0)//处理当在首行时的边界条件
{
dp[i][j][k]=dp[i][j-1][k]+map[i][j]+map[k][i+j-k];//只可能有一种情况
}
else if(j==0)//处理在首列时候的边界条件
{
continue;//不存在这种情况
}
else
{
dp[i][j][k]=max(max(dp[i-1][j][k],dp[i-1][j][k-1]),dp[i][j-1][k])+map[i][j]+map[k][i+j-k];//三种情况都考虑
}
}
else
{
if(i==0)//同上的都不解释了
{
dp[i][j][k]=max(dp[i][j-1][k-1],dp[i][j-1][k])+map[i][j]+map[k][i+j-k];
}
else if(j==0)
{
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j][k-1])+map[i][j]+map[k][i+j-k];
}
else
{
dp[i][j][k]=max(max(dp[i-1][j][k],dp[i-1][j][k-1]),max(dp[i][j-1][k-1],dp[i][j-1][k]))+map[i][j]+map[k][i+j-k];
}
}
}