​题目传送门​

一、走两次行不行

这是一种类似贪心的解法,第一次选择最大的,然后把最大路径上的数字都置为空,第二次再选择最大的。

这就是只见树木不见森林的方法了:

第一次走为局部最优并且也对第二次走造成了影响,第二次走是在第一次影响下所能走的局部最优,不具备“无后效性”,因此分开两次走并不是全局最优解。

不过如果先取一次最大路径、抹掉数字后再取一次最大路径(即两次\(dp\))是错的,可以举一个反例来证明,

9 0 3
0 9 2
0 2 0

最长路径是\(9+9+2=20\),假设这次走的路线是第一行的\(9\)、第二行的\(9\)和\(2\),那么在第二次走的时候,地图变成

0 0 3
0 0 0
0 2 0

显然最长路径是\(3\),总和为\(23\)。

然而这不是最优解,最优解是第一次走第一行的\(9\)、第二行的\(9\)、第三行的\(2\)(仍是\(20\)),但第二次可以走第一行的\(3\)、第二行的\(2\),得到\(5\),总和达到\(25\)。

二、四维状态表示

1、状态表示:

\(f[x1][y1][x2][y2]\)表示第一条路径走到\(x1,y1\)、第二条路径走到\(x2,y2\)时的最大值

2、状态转移:

那么它的上一个状态只能是下面四个中的某一个:

\(x1-1,x2-1\)(下下)

\(x1-1,y2-1\)(下右)

\(y1-1,x2-1\)(右下)

\(y1-1,y2-1\)(右右)

那到底是从哪个状态迁移来的呢?因为我们想要取此状态的最大值,所以很显然,四个前序状态,哪个数大要哪个。

#include <bits/stdc++.h>

using namespace std;
const int N = 15;

int n; //方格的宽度和高度
int w[N][N]; //每个方格里面的数字
int f[N][N][N][N]; //四维的DP数组

int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n;
//接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
int a, b, c;
//一行 0 0 0 表示结束。
while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
//开始递推
for (int x1 = 1; x1 <= n; ++x1)
for (int y1 = 1; y1 <= n; ++y1)
for (int x2 = 1; x2 <= n; ++x2)
for (int y2 = 1; y2 <= n; ++y2) {
//减枝
if (x1 + y1 != x2 + y2) continue;
//PK获取到最优的上一个状态
int t = f[x1 - 1][y1][x2 - 1][y2];
t = max(t, f[x1][y1 - 1][x2][y2 - 1]);
t = max(t, f[x1 - 1][y1][x2][y2 - 1]);
t = max(t, f[x1][y1 - 1][x2 - 1][y2]);
//加上这个点的数值
f[x1][y1][x2][y2] = t + w[x1][y1];
//如果这个点没有被重复走,那么再加一次
if (x1 != x2 && y1 != y2) f[x1][y1][x2][y2] += w[x2][y2];
}

printf("%d", f[n][n][n][n]);
}

三、三维状态表示

#include <bits/stdc++.h>

using namespace std;
const int N = 15;

int n;
int w[N][N];
int f[N * 2][N][N];

int main() {
cin >> n;
//接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
int a, b, c;
//一行 0 0 0 表示结束。
while (cin >> a >> b >> c, a || b || c) w[a][b] = c;

//左上角是(1,1),k表示两个小朋友所在位置的x+y的和,最多是2*n
for (int k = 2; k <= 2 * n; k++)
for (int x1 = 1; x1 <= n; x1++)//第一个小朋友竖着走的距离
for (int x2 = 1; x2 <= n; x2++) {//第二个小朋友竖着走的距离
int y1 = k - x1, y2 = k - x2;//计算横着走的距离
//不能出界,只走有效的位置
if (y1 >= 1 && y1 <= n && y2 >= 1 && y2 <= n) {
//PK获取到最优的上一个状态
int t = f[k - 1][x1 - 1][x2];
t = max(t, f[k - 1][x1 - 1][x2 - 1]);
t = max(t, f[k - 1][x1][x2 - 1]);
t = max(t, f[k - 1][x1][x2]);
//将本位置的数值加上
f[k][x1][x2] = t + w[x1][y1];
//如果不是重复的位置,还可以继续加上
if (x1 != x2) f[k][x1][x2] += w[x2][y2];
}
}
//输出DP的结果
printf("%d\n", f[2 * n][n][n]);
return 0;
}

四、挖坑等待填

此题的终极解法是“​​k以方格数​​”,\(k<=10\),如果还用上面的思路就不行了,因为上面的维护是\(2^n\),如果是\(k=10\),不是\(1024\)维,肯定是不行的,那需要用到最小费用流,以后再填坑吧。