方格取数原题链接https://www.luogu.com.cn/problem/P1004 通过题意我们可以可以看出这是一道动态规划的题,第一想法是用二维的话只能dp出最大值的路径,再用一次二维dp求出次大值的路径,这样的想法是错误的!题目要求的是两条路径走到右下角的和的最大值!如果是按照刚刚的想法,先求最大值的路径,再求次大值的路径,最大值确实是最大值,但是次大值就不一定了,为了走出最大值就不得不让次大值小。可以自己举个例子看看。
也就是说这两步不能分成两次规划,要让和的最大值在一个规划中约束两条路径的生成。那么怎么在一个动态规划中表示两条路径呢,我们用二维只能表示一条,那我们再用两个纬度来表示第二条路径不就好了吗?这样四维状态方程也就被构想出来了。即,我们用f[i][j][k][l]作为状态方程,表示第一个人走到(i,j)时,第二个人走到(k,l),f[i][j][k][l]表示的是取到物品的价值和的最大值。
怎么表示状态转移呢?两个人从同一起点出发,每个人都有两种方向可以走,那么两个人就有四种方向:
- 第一个人向右,第二个人向下
- 两个都向右
- 第一个人向下,第二个人向右
- 两个人都向下
也就是从要走到(i,j)和(k,l)有上述四个方法,那么状态转移就表示为从上述四个方向来的和的最大值加上(i,j)和(k,l)上的物品的价值:
最终的答案就是:f[n][n][n][n],两个人都走到了右下角
这里要注意:当第一个人和第二个人走到了同一个点时,也就是i==k,j==l,因为物品只有一个,被一个人捡走了就没了,所以要特判一下,减去状态转移方程中多加的一个
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int a[N][N], f[N][N][N][N];
int n, u, v, w;
int main()
{
cin >> n;
while (cin >> u >> v >> w)
{
if (!u && !v && !w)
break;
a[u][v] = w;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
for (int l = 1; l <= n; l++)
{
f[i][j][k][l] = max(max(f[i][j - 1][k][l - 1], f[i][j - 1][k - 1][l]), max(f[i - 1][j][k][l - 1], f[i - 1][j][k - 1][l])) + a[i][j] + a[k][l];
if (i == k && j == l)
f[i][j][k][l] -= a[i][j];
}
cout << f[n][n][n][n];
return 0;
}