【学习笔记】四维dp

NCC79601

2019-09-04 23:11:21

Personal

# 四维dp 四维dp可以用来解决在行走方向固定的情况下,在一个矩阵内从$(1,1)$到$(n,m)$的两条不同路径的某些信息。 --- **例题** [P1004](https://www.luogu.org/problemnew/show/P1004) # 分析 题面意思是给出一个$n\times n$的矩阵,从$(1,1)$开始取数,每次只能向右走或者向下走,数被取走后就没有了,求两次取数能够得到的最大和。 这就是 **四维dp**。 用$f[i][j][k][l]$表示第一次走到$(i,j)$,第二次走到$(k,l)$时能获得的最大和,那么由于只能向右走和向下走,所以每种状态仅有四种可能的前驱。所以转移方程就可以写成: $$f[i][j][k][l]=max\{f_{pre}\}+a[i][j]+a[k][l]$$ 特别地,如果$i=k$且$j=l$,则$f[i][j][k][l]$需要减去$a[i][j]$(每个数被取以后就变成$0$了)。 最终,在$f[n][n][n][n]$处获得答案。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 10; int n; ll a[MAXN][MAXN], f[MAXN][MAXN][MAXN][MAXN]; int main() { scanf("%d", &n); for(int x = 1, y; x; ) { scanf("%d %d", &x, &y); scanf("%lli", &a[x][y]); } 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 - 1][j][k - 1][l], f[i - 1][j][k][l - 1]), max(f[i][j - 1][k - 1][l], f[i][j - 1][k][l - 1]) ) + a[i][j] + a[k][l]; if(i == k && j == l) f[i][j][k][l] -= a[i][j]; } printf("%lld", f[n][n][n][n]); return 0; } ``` **例题** [P1006](https://www.luogu.org/problemnew/show/P1006 ) # 分析 题面意思是给出一个$n\times m$的矩阵,需要找出从$(1,1)$到$(n,m)$的两条不重合路径。 和 P1004 非常相似,也可以用四维 dp 来解决这道题(好像实际上可以优化到三维,不过我还是算了)。这道题和 P1004 的不同之处在于,P1004 是每个格子可以走多次,而这道题每个格子只能走一次。 其实这两种情况并没有什么区别,同样用$f[i][j][k][l]$表示走到$(i,j)$和$(k,l)$能得到的最大好感度。因为对于$i=k$且$j=l$的情况,将$f[i][j][k][l]$减去$a[i][j]$即表示第二次经过$(i,j)$没有意义,也就是获得的好感度为$0$。可以发现第二条路经任何不同于$(i,j)$的方格都能比经过$(i,j)$更优,那么这种状态在转移过程中自然就被舍弃掉了。所以,这里使用和 P1004 相同解法的正确性是显然的,可以直接无脑进行四维dp。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 60; int n, m; ll a[MAXN][MAXN], f[MAXN][MAXN][MAXN][MAXN]; int main() { scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%lli", &a[i][j]); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int k = 1; k <= n; k++) for(int l = 1; l <= m; l++) { f[i][j][k][l] = max( max(f[i - 1][j][k - 1][l], f[i - 1][j][k][l - 1]), max(f[i][j - 1][k - 1][l], f[i][j - 1][k][l - 1]) ) + a[i][j] + a[k][l]; if(i == k && j == l) f[i][j][k][l] -= a[i][j]; } printf("%lli", f[n][m][n][m]); return 0; } ```