=== 四维DP的学习 ===
>>> 2025-11-23
例题:洛谷P1004方格取数 四维DP
两条路线同时规划:
x1、y1、x2、y2
由于最后同时到达,所以x1+y1 = x2 + y2 = k(自己设的)
y1 = k - x1,y2 = k - x2。总体来看可以省一个变量
所以需要的就为k,x1,x2三个变量
k的范围为[2,2N] {(0,0)到(1,1)和(0,0)到(N,N)}
grid[x][y]是(x,y)的值
dp[k][x][y] 是两条路线的和
dp[2][1][1] = grid[1][1]
行号i必须满足 1 <= i <= N 且 1 <= c1 <= N,c1 = k - i 即 k - n <= i <= k - 1
即i >= 1 且 i >= k - n
i需要大于这里两个最大的
然后开始枚举第一条路径和行号和列号
在满足第一条路径的前提下枚举第二条路径的行号和列号
计算每一个可能的情况(第一条路径行与列和第二条路径行与列同时满足的情况)能拿到的数字总和
开始分情况讨论:
①走到同一个格子时,只能取一次该格子的值
②走到不一样的格子时,此时这种情况的值是两个格子的和
然后开始状态转移
开始比较上一步的四种可能,取最大的结果
①两条路径都从上方走来
②第一条路径从上方走来,第二条路径从左方走来
③第一条路径从左方走来,第二条路径从上方走来
④两条路径都从左方走来
这里的判断过程用到了i,j的值。因为只有第一行的没有上一行。
通过这四种可能找到上一步的最优状态。
接着将上一步的最优状态加上这一步的情况值,就得到了这一步的最优状态。
最终两条路径都到达(N,N),cout 一下··dp[2 * N][N][N]的值就得到了最大值。
接下来是AI代码实现
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 10;
int grid[MAXN][MAXN] = {0};
int dp[2*MAXN][MAXN][MAXN]; //dp[k][i][j]
int main()
{
int n = 0;
cin >> n;
memset(grid,0,sizeof(grid));
while(1)
{
int x,y,val;
cin >> x >> y >>val;
if(x == 0 && y == 0 && val == 0)
break;
grid[x][y] = val;
}
fill(dp[0][0],dp[0][0] + n * n,0);
dp[2][1][1] = grid[1][1];
for(int k=3;k<=2 * n;k++)
{
for(int i = max(1,k - n);i<=min(n,k - 1);i++)
{
int c1 = k - i; //第一条路径的列号
//j:第二条路径的行号
for(int j = max(1,k - n);j <= min(n,k - 1);j++)
{
int c2 = k - j;
//获取当前格子的值(同格只取一次)
int val = (i==j) ? grid[i][c1] : grid[i][c1] + grid[j][c2];
int best = 0;
//两条都从上方来
if(i > 1 && j > 1) best = max(best,dp[k-1][i-1][j-1]);
//第一条从上,第二条从左
if(i > 1) best = max(best,dp[k-1][i-1][j]);
if(j > 1) best = max(best,dp[k-1][i][j-1]);
best = max(best,dp[k-1][i][j]);
dp[k][i][j] = best + val;
}
}
}
cout << dp[2*n][n][n] << endl;
return 0;
}
我的复现
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 10;
int grid[MAXN][MAXN] = {};
int dp[2 * MAXN][MAXN][MAXN] = {}; //k,x1,x2
int main()
{
//行数,统计特殊点的位置和坐标
int n = 0;
cin >> n;
while(1)
{
int x,y,a = 0;
cin >> x >> y >> a;
if(x == 0 && y == 0)
{
break;
}
grid[x][y] = a;
}
dp[2][1][1] = grid[1][1]; //起步 到起点的点数等于起点的值
for(int k = 3;k <= 2 * MAXN;k++)
{
for(int i = max(1,k-n);i <= min(n,k-1);i++) //限制第一条路径的行数列数
{
for(int j = max(1,k-n);j <= min(n,k-1);j++) //第二条路径同理
{
int current_value = 0;
current_value = grid[i][k-i] + grid[j][k-j]; //这里细节先写这个,因为如果第二个不满足就不用再写了
if(i == j)
current_value = grid[i][k-i];
//开始状态转移!!!!
//第一种情况
int max_num = 0; //这里看的是上一步最大
if(i > 1 && j > 1) //all down
{
max_num = max(max_num,dp[k-1][i-1][j-1]);
}
if(i > 1) // 1st down
{
max_num = max(max_num,dp[k-1][i-1][j]);
}
if(j > 1) //2nd down
{
max_num = max(max_num,dp[k-1][i][j-1]);
}
max_num = max(max_num,dp[k-1][i][j]); //neither down
dp[k][i][j] = current_value + max_num; //该点的DP值等于current_value(该点的值)+max_num(先前DP值最大的可能)
}
}
}
cout << dp[2 * n][n][n] << endl;
return 0;
}
这应该是我目前做过最难的题,啊! 2025年11月23日 19点58分 L2-406