先从动规刷起 ,做了这么一道题,2000年的老题,提高组第四题
题目直接限制了只能向下或向右走,换句话说就是每个点只能由上或由左走到。因此,如果只考虑一条路径,还是比较直观就能想明白:
设F[i][j]表示走到i行j列时的最大值
则F[i][j]=max{F[i-1][j],F[i][j-1]} +a[i][j]//F[i][j]只能由这两格转移来
只考虑一条路径就可以这么写:
1 | for(i=1;i<=n;i++) |
虽然,题目要求又多了一条路径,但是如果沿着这个思路,也就不难想了,只是需要四维数组,分别记下两条路径。而下一个状态,应该是以四种状态中的最大值转移得来。由于题目限制n<=10,于是就可以不用犹豫使用这样的四种循环动规求解了,列出动态转移方程:
设F[i][j][k][l]表示第一条路径走到i行j列,第二条路径走到k行l列时的最大值
则F[i][j][k][l]=max{F[i-1][j][k-1][l],F[i][j-1][k-1][l],F[i-1][j][k][l-1],F[i][j-1][k][l-1]}+a[i][j]+a[k][l]
//分别是第一条路径的两种和第二条路径的两种,乘法原理
因此,最后的写法就是个四重循环O(n^4)//n<=10
1 | for(i=1;i<=n;i++) |
这里还有一个容易忽视的细节,i,j,k,l重合时,a[i][j]只能被加一次//边界条件
这题尽管不是很难理解,但利用了n<=10这一条件,用四维数组DP的做法还是很巧妙。
居然还有四维压三维的做法
之前的F[i][j][k][l]四维做法,会发现有许多冗余计算:对于每一组路径上各取一点的所有组合都会被重复计算。
我们可以发现因为只能向下或向右,两条路径的长度是确定的,而设共走了k步(以左上角为第一步),对于每一个k,点(i,j)的坐标都有i=k-j+1,因此就可以以一个k代替原来的两个横坐标(或纵坐标)。
于是可以进行三维的优化:
设F[i][j][k]表示第一条路径走到第i列,第二条路径走到第j列,共走了k步时的最大值
这样,动态转移方程就是:
F[i][j][k]=max(F[i-1][j][k-1],F[i][j-1][k-1],F[i][j][k-1],F[i-1][j-1][k-1])+a[i][k-i+1]+a[j][k-j+1]
//F[i][j][k]由上一步k-1的四个点的最大值转移,四个点与四维做法相同
还有一点是,由于记录的是步数,对应步数下的i,j取值范围改变,应为[1,k] (k<=n),[k-n+1,n] (k>n)
所以,最后的代码O(n^3):
1 | f[1][1][1]=map[1][1]; |
、、、
这就很妙了( ̄▽ ̄)~*