题目
思路
一开始dp了两次,错误,然后又尝试第一次dp后在进行回溯,把第一次最优路径中的点更改为\(0\), 然后第二次dp,在把两次和相加,这样是错的。
这两个子最优结构并不同属一个最优结构,这两个子最优结构只能保证单次最优,然后再单次最优的情况下在进行求解,这并不等于两次最优。
例如如下路径
4 * 4 矩阵
0 0 2 3 0 0 0
0 0 3 0 0 0 0
0 0 3 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 4 0 0
0 0 0 0 4 0 0
0 0 2 0 4 0 0
dp的话第一次路径应该是 2->3->3->4->4->4
第二次是 3
但是最优路径是这样的 第一次:2->3->3->2->4
第二次 3->4->4
当然直观的理解,也能想到这种做法显然错误(因为我本人CPU不好使😂)
我们将上述问题转化为两个人同时走,并且保证一个点的值只能取走一次的问题,可以肯定这两个问题等价:
不妨设\(f[x_1][y_1][x_2][y_2]\) 表示当第两个人走到\((x_1, y_1)\), \((x_2, y_2)\)时的最优解,考虑到约束条件同时即\(x_1 + y_1 = x_2 + y_2\), 我们设\(k = x_1 + y_1 = x_2 + y_2\), 那么状态可以缩小为\(f[k][x_1][x_2]\) , 表示\((x_i, k - x_i)\).
那么对于状态转移, 对于\((x_i, y_i)\)有两个状态可以转移过来, 所以总共有四个状态可以转移过来。
\((x_1 - 1, y_1), (x_2 - 1, y_2) = f[k - 1][x_1 - 1][x_2 - 1]\)
\((x_1 - 1, y_1), (x_2, y_2 - 1) = f[k - 1][x_1 - 1][x_2]\)
\((x_1, y_1 - 1), (x_2, y_2 - 1) = f[k - 1][x_1][x_2]\)
\((x_1, y_1 - 1), (x_2 - 1, y_2) = f[k - 1][x_1][x_2 - 1]\)
\(f[k][x_1][x_2] = max(f[k - 1][x_1 - 1][x_2 - 1], f[k - 1][x_1 - 1][x_2], f[k - 1][x_1][x_2], f[k - 1][x_1][x_2 - 1]) + (a[x_1][k - x_1] + a[x_2][k - x_2])\)
另外还需要判断当前的点是否存在重合,这样递推过来保证我们的子集也不存在重合的。
Code
第一次提交 WA
#include <bits/stdc++.h>
#define rep(i, j, k) for(int i = j; i <= k; i ++)
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<int>> a(n + 1, std::vector<int>(n + 1));
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(n + 1));
std::vector<std::vector<int>> dp2(n + 1, std::vector<int>(n + 1));
int _x, _y, _val;
while(std::cin >> _x >> _y >> _val && (_x || _y || _val)) {
a[_x][_y] = _val;
}
rep(i, 1, n) {
rep(j, 1, n) {
dp[i][j] = std::max(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
}
}
int xi = n, yi = n;
rep(i, 1, n) {
rep(j, 1, n) {
std::cout << a[i][j] << " \n"[j == n];
}
}
puts("-------------------------------------");
rep(i, 1, n) {
rep(j, 1, n) {
std::cout << dp[i][j] << " \n"[j == n];
}
}
while(xi != 1 || yi != 1) {
if(xi - 1 == 0) yi --, a[xi][yi] = 0;
else if(yi - 1 == 0) xi --, a[xi][yi] = 0;
else if(dp[xi - 1][yi] >= dp[xi][yi - 1]) {
xi --;
a[xi][yi] = 0;
} else {
yi --;
a[xi][yi] = 0;
}
}
puts("-------------------------------------");
rep(i, 1, n) {
rep(j, 1, n) {
std::cout << a[i][j] << " \n"[j == n];
}
}
// std::cout << xi << " " << yi << "\n";
a[n][n] = 0;
rep(i, 1, n) {
rep(j, 1, n) {
dp2[i][j] = std::max(dp2[i - 1][j], dp2[i][j - 1]) + a[i][j];
}
}
std::cout << dp[n][n] << "\n";
std::cout << dp[n][n] + dp2[n][n] << "\n";
}
int main() {
int _;
_ = 1;
while(_ --) {
solve();
}
}
第二次AC:
#include <bits/stdc++.h>
#define rep(i, j, k) for(int i = j; i <= k; i ++)
using i64 = long long;
int dp[100][100][100];
int dx[]{-1,-1,0,0}, dy[]{-1,0,0,-1};
void solve() {
int n;
std::cin >> n;
memset(dp, 0, sizeof dp);
std::vector<std::vector<int>> a(n + 1, std::vector<int>(n + 1));
int _x, _y, _val;
while(std::cin >> _x >> _y >> _val && (_x || _y || _val)) {
a[_x][_y] = _val;
}
auto check = [&n](int x1, int y1)->bool{
return x1 >= 1 && x1 <= n && y1 >= 1 && y1 <= n;
};
rep(k, 2, 2 * n) {
rep(x1, 1, n) {
rep(x2, 1, n) {
int y1 = k - x1, y2 = k - x2;
if(x1 == x2) {
dp[k][x1][x2] += a[x1][k - x1];
} else {
dp[k][x1][x2] += a[x1][k - x1] + a[x2][k - x2];
}
int mx = -1;
if(check(x1, y1) && check(x2, y2)) {
rep(i, 0, 3) {
mx = std::max(mx, dp[k - 1][x1 + dx[i]][x2 + dy[i]]);
}
dp[k][x1][x2] += mx;
}
}
}
}
std::cout << dp[2 * n][n][n] << "\n";
}
int main() {
int _;
_ = 1;
while(_ --) {
solve();
}
}