刚开始的想法是跑两遍dp 第一次找权值最大的路径 然后把走过的路径标记为负无穷大 然后在走一遍就好了 但是后来发现如果要让两次的和最大 那么第一次的路线可能不是最大的一条路 所以并不能这么写 看了别人的题解 题解我都看了两天 真是有意思
思路是这样的 我们假设有两个小人从左上角走到右下角 不同路线走 不相交 就代替了这个模型 用一个dp[i][j][k] 来表示当走了I步时其中一个人的x坐标为j另一个人的x坐标为k
不难想到当前状态可以来自四种其他状态 分别是小人一号来自左边 小人二号来自上边 或者小人一号来自左边小人二号来自左边 或者小人一号来自上边小人二号来自左边 或者小人一号来自上边小人二号来自上边 就这样 转移方程是 :dp[i][j][k] = max(max(dp[I - 1][j][k], dp[I - 1][j - 1][k]), max(dp[I - 1][j - 1][k - 1], dp[I - 1][j][k - 1]));
我写这道题的时候把储存权值的表定义成了char类型 然后debug一早上 真是醉了
参考了这个人的博客 写的很详细
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[110][55][55];
int main()
{
int t, n, m;
int str[55][55];
cin >> t ;
while ( t -- ) {
cin >> n >> m ;
for (int i = 1;i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> str[i][j] ;
memset(dp , 0, sizeof(dp));
for (int i = 1; i < m + n ; i ++) {
for (int j = 1; j <= min(n , i); j ++) {
int y1 = i - j + 1;
if(y1 > m)continue;
for (int k = j +1; k <=min(i , n); k ++) {
int y2 = i - k + 1;
int temp = -1;
temp = max(temp , dp[i - 1][j - 1][k - 1]);
temp = max(temp , dp[i - 1][j - 1][k]);
temp = max(temp , dp[i - 1][j][k - 1]);
temp = max(temp , dp[i - 1][j][k]);
dp[i][j][k] = temp + str[j][y1] + str[k][y2];
}
}
}
int ans = 0;
ans = max (ans , dp[m + n - 2][n - 1][n]);
ans = max (ans , dp[m + n - 2][n][n - 1]);
cout << ans << endl;
}
}