题义是两个人分别从左上角和右下角互相传递纸条,但是可以将问题转化为同时在左上角向下传递的两个纸条,求这两个不相交的路径的最大权值。
TLE了,但总算是把思维的DP弄懂了,总是在路线重复上觉得这个dp方程存在问题,后面才恍悟,在两个纸团坐标相同的点的dp值总是为零的,因为它从头到尾都没有更新。
定义的dp方程f[i][j][k][p]是指在1,1这个点同时抛的两个纸团的具体坐标(i,j)和(k,p),方程保证不更新两个点相同的值,方程是这样的:
f[i,j,k,p]:=max{f[i-1,j,k-1,p]
f[i-1,j,k,p-1]
f[i,j-1,k-1,p]
f[i,j-1,k,p-1]
} + G[i,j] + G[k,p]
网上有个三维优化的。。。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> #include <algorithm> #define MAXN 55 using namespace std; int N, M, dp[MAXN][MAXN][MAXN][MAXN]; int G[MAXN][MAXN]; inline bool judge(int i, int j, int k, int p) { if (i==k && j==p) { if (i==N && j==M) { return true; } else return false; } else return true; } inline int Max(int i, int j, int k, int p) { int mm = dp[i-1][j][k-1][p]; mm = max(mm, dp[i-1][j][k][p-1]); mm = max(mm, dp[i][j-1][k-1][p]); mm = max(mm, dp[i][j-1][k][p-1]); return mm; } void DP() { memset(dp, 0, sizeof (dp)); for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { for (int k = 1; k <= N; ++k) { for (int p = 1; p <= M; ++p) { if (judge(i, j, k, p)) { // puts("JKLJL"); dp[i][j][k][p] = Max(i, j, k ,p) + G[i][j] + G[k][p]; } } } } } printf("%d\n", dp[N][M][N][M]); } int main() { while (scanf("%d %d", &N, &M) == 2) { for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { scanf("%d", &G[i][j]); } } DP(); } }
以下是三维的DP思路,由于从左上角走到右下角只有唯一的步数可能(step = N+M-2),只要知道走了多少步并且所在行数就能够推出所在的列数,因此,状态由四维变成了三维,f[s][i][j], s表示总共走了多少步,i表示第一个纸条所在的行数,j表示第二个纸条所在的行数,由前面已知有步数和行数便能够得出列数,所以dp方程和前面的是差不多的。
f[s][i][j] = max {
f[s-1][i][j], // 表示均从左边传递过来的纸条
f[s-1][i-1][j], // 表示第一个纸条从上面传递过来,第二个纸条从左边传递
f[s-1][i][j-1], // 表示第一个纸条从左边传递过来,第二个纸条从上面传递
f[s-1][i-1][j-1] // 表示均从上面传递过来
}
代码如下:
#include <cstdlib> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAXN 55 using namespace std; int N, M, G[MAXN][MAXN]; int dp[105][MAXN][MAXN]; // 定义了三维数组,f[s][i][j] s表示步数,i表示第一个纸团到达的行,j表示第二个纸团到达的行 inline bool judge(int s, int i, int j) { int y1 = s - i + 2; int y2 = s - j + 2; // 这里求出了两个纸团所在位置的纵坐标 if (y1 > M || y2 > M || y1 < 1 || y2 < 1) { return false; } if (i == j) { if (s == N + M - 2 && i == N) { return true; } else { return false; } } else { return true; } } inline int Max(int s, int i, int j) { int mm = dp[s-1][i][j]; mm = max(mm, dp[s-1][i-1][j]); mm = max(mm, dp[s-1][i][j-1]); mm = max(mm, dp[s-1][i-1][j-1]); return mm; } void DP() { int step = N + M - 2; memset(dp, 0, sizeof (dp)); dp[0][1][1] = 2 * G[1][1]; for (int s = 1; s <= step; ++s) { // 总共走step步 for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { if (judge(s, i, j)) { dp[s][i][j] = Max(s, i, j)+G[i][s-i+2]+G[j][s-j+2]; } } } } printf("%d\n", dp[step][N][N]); } int main() { while (scanf("%d %d", &N, &M) == 2) { for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { scanf("%d", &G[i][j]); } } DP(); } return 0; }