luogu 1004 方格取数 dp

题目链接

题意

设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示:

A
 0  0  0  0  0  0  0  0
 0  0 13  0  0  6  0  0
 0  0  0  0  7  0  0  0
 0  0  0 14  0  0  0  0
 0 21  0  0  0  4  0  0
 0  0 15  0  0  0  0  0
 0 14  0  0  0  0  0  0
 0  0  0  0  0  0  0  0
                        B

某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。

此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

思路

如果采取走两次分别保证最优的做法,得到的答案不一定总和最优。

正解是考虑三重循环,模拟两个人同时向前走。最外层循环为当前已走的步数,里面两层循环分别为第一个人走到的行数和第二个人走到的行数。即(dp[k][i][j])表示当前走了(k)步,第一个人的坐标为((i,k-i)),第二个人的坐标为((j,k-j)).

又因为第(k+1)步只可能由第(k)步转移过来,因此可以采取类似背包问题的简化方式,将第一维省去。

这种外层循环为步数,逐步向前走的做法,保证了不会出现一个人走到另一个人以前走过的路径上的情况,路径重合只可能发生在当前走到同一个格子上,而这种情况是很好处理的,计算一次即可。
// 讲道理我不是很明白那些写四重循环的为什么是对的。

Code

#include <bits/stdc++.h>
#define maxn 10
using namespace std;
typedef long long LL;
int a[maxn][maxn], dp[maxn][maxn];
int main() {
    int n, x, y, k;
    scanf("%d", &n);
    while (scanf("%d%d%d", &x, &y, &k) && x) a[x][y] = k;
    for (int k = 2; k <= (n<<1); ++k) {
        for (int i = min(k-1, n); i >= 1; --i) {
            for (int j = min(k-1, n); j >= 1; --j) {
                dp[i][j] = max(dp[i-1][j-1], max(dp[i-1][j], max(dp[i][j-1], dp[i][j]))) + a[i][k-i] + (i!=j)*a[j][k-j];
            }
        }
    }
    printf("%d
", dp[n][n]);
    return 0;
}

原文地址:https://www.cnblogs.com/kkkkahlua/p/7632540.html