洛谷 - P1009 - 传纸条(多维DP)
题目链接 : https://www.luogu.com.cn/problem/P1006
想到了用四维数组来枚举的,但是不太明白如何判重。后来才明白,只需要令 x2 > x1 即可
先来分析状态转移方程 :
dp[ x1 ][ y1 ][ x2 ][ y2 ] = max( max( dp[ x1 + 1 ][ y1 ][ x2+1 ][ y2 ] , dp[ x1 ][ y1+1 ][ x2 ][ y2+1 ] )
max( dp[ x1+1 ][ y1 ][ x2 ][ y2+1 ] , dp[ x1 ][ y1+1 ][ x2+1 ][ y2 ] )
) + board[ x1 ][ y1 ] + board[ x2 ][ y2 ]
它表示,对于某一个时刻, A在位置 ( x1,y1 ) , B在位置( x2,y2 ),它可以由4种途径得到:分别为 A的两种方式到达 (x1,y1) 和 B的两种方式到达( x2,y2 )的四种组合。最后加上新得到的分数
接下来还剩下三个问题:
< 1 > dp初值条件:这个好说,就是 0。(补充一点:题目要求来回传纸条,等价于一个人传给另一个人两张纸条。这样可以方便的固定dp起点 )
< 2 > dp的循环顺序:由于是从 右下角的(x,y) 走到左上角的(1,1) , 每一个靠左上方的点的状态,都需要由靠右方、下方的点的状态推得,故变量由 x -> 1, y -> 1,表示从右下向左上推导。
< 3 > dp终点?由于我们控制了 x2 > x1 , 故最终不可能得到 dp[1][1][1][1]的状态,而应该是即将变为dp[1][1][1][1]的状态,也就是 --> dp[2][1][1][2]
另外:
四层嵌套for循环的合理性?
首先在避免重复的这一点上肯定是合理的(限制了x2 > x1)
现在只需要考虑: 为什么这样的循环顺序,即现枚举x1的一个状态,再枚举所有的x2状态这种做法合理?
动手画画图可以知道,实际上遍历的顺序是 -> 外两层循环在按照从右到左、从下到上的顺序遍历每一个A的位置 (x1,y1),内两层循环针对每一种(x1,y1)遍历所有 x1 右边的矩形范围内的点(x2,y2)。因此一定可以得到每一种无交点的传递纸条途径。
循环最终得到的x1的最终位置为(1,1),x2的最终位置为(2,1)。但是由于 (x1,y1)为(1,1)时,按照我们的状态方程,它会将 1,2纳入考虑范围内,但是实际上A不可能走到(1,2),否则会与B的路径相交,故最终答案存储在 dp[2][1][1][2] 中。
1 #include <iostream> 2 #define Maxsize 5 3 using namespace std; 4 int board[Maxsize][Maxsize]; 5 int f[Maxsize][Maxsize][Maxsize][Maxsize]; 6 // x1 y1 x2 y2 7 int main(){ 8 int x,y; 9 cin >> x >> y; 10 for(int i = 1; i <= x; i++){ 11 for(int j = 1; j <= y; j++){ 12 cin >> board[i][j]; 13 } 14 } 15 for(int x1 = x; x1 >= 1; x1--) 16 for(int y1 = y; y1 >= 1; y1--) 17 for(int x2 = x1 + 1; x2 >= 1; x2--) 18 for(int y2 = y; y2 > y1; y2--){ 19 f[x1][y1][x2][y2] = max( 20 max( f[x1+1][y1][x2+1][y2] , f[x1][y1+1][x2][y2+1] ), 21 max( f[x1+1][y1][x2][y2+1] , f[x1][y1+1][x2+1][y2] ) 22 ) 23 + board[x1][y1] + board[x2][y2]; 24 } 25 cout<<f[1][1][1][2]; 26 cout<<f[2][1][1][2]; 27 return 0; 28 }