C. Relay Race
标签:dp
题意:
- 给出一个边长为n(1 ≤ n ≤ 300)的矩阵,每个方格中有一个数( - 1000 ≤ x ≤ 1000)
- 小A从(1,1)移动到(n,n),小B从(n,n)移动到(1,1)
- 上述条件实际上等价于两人从(1,1)移动到(n,n)
- 求两人移动过程中覆盖方格中的数字的最大总和,两人都经过的方格只能加一次
思路:
由于题目所求是两个人在矩阵中移动的覆盖值最大,我们首先想到的是四维dp,令每个维度分别表示两人的坐标,但是空间不够
矩阵大小为n*n时,从左上角到右下角的步数为2 * (n-1)
观察到步数与横纵坐标的关系,我们尝试将dp优化到三维dp[step][x][i]
step
表示两人分别走了step
步
x
,i
分别表示两人位于第几行
可推 y = step-x+2
,j = step-i+2
,即两人的坐标分别为(x,y),(i,j)
转移方程比较简单,看下面注释即可
方格中的数字会出现负数,因此dp数组要初始化一下
代码:
#define fst std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout << std::fixed << std::setprecision(20)
#define le "\n"
#define ll long long
#include <bits/stdc++.h>
using namespace std;
const int N=300+5;
const int mod=1e9+7;
int a[N][N];
int dp[2*N][N][N];//step表示两人分别走了step步 x i分别表示两人位于第几行
int main() {
int n; cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
memset(dp,-0x3f,sizeof(dp));
dp[0][1][1] = a[1][1];
for(int step=1;step<=2*(n-1);step++){
for(int x=1;x<=n;x++){
for(int i=1;i<=n;i++){
int j = step-i+2, y = step-x+2;
if(j<1 || j>n || y<1|| y>n) continue;
dp[step][x][i] = max(dp[step][x][i],dp[step-1][x-1][i]+a[i][j]+a[x][y]);//A向下走,B向右走
dp[step][x][i] = max(dp[step][x][i],dp[step-1][x][i-1]+a[i][j]+a[x][y]);//A向右走,B向下走
dp[step][x][i] = max(dp[step][x][i],dp[step-1][x-1][i-1]+a[i][j]+a[x][y]);//A向下走,B向下走
dp[step][x][i] = max(dp[step][x][i],dp[step-1][x][i]+a[i][j]+a[x][y]);//A向右走,B向右走
if(x==i&&y==j) dp[step][x][i] -= a[i][j];//两人位置重复,减去重复计算
}
}
}
cout<<dp[2*(n-1)][n][n]<<le;
return 0;
}