CF213C Relay Race 题解

题意

n×nn\times n 的矩形,价值为 ai,ja_{i,j}
现在你要从 a1,1a_{1,1} ,通过移动到 ai,j+1,ai+1,ja_{i,j+1},a_{i+1,j} 的方式,走到 an,na_{n,n}
再通过移动到 ai,j1,ai1,ja_{i,j-1},a_{i-1,j} 的方式,回去 a1,1a_{1,1}。定义价值为路径上 ai,ja_{i,j} 的和,重复走过的路径只计算一次。

问最大的价值是多少?


思路

错解

首先,猜个结论:先从 1,11,1 跑 dp。然后按路径将 aia_i 设为 00。在从 an,na_{n,n} 跑 dp。

但是这样做是错误的。

3
5 5 1
0 5 -2
5 -4 -2

dp 会跑出来 11+111+1 的结果。但是正解却是 4+104+10

dp:

(1,1)(1,2)(2,2)(2,3)(3,3)(3,2)(3,1)(2,1)(1,1)(1,1)\to(1,2)\to(2,2)\to(2,3)\to(3,3)\to(3,2)\to(3,1)\to(2,1)\to(1,1)
5+5+522=11,04+5+0+0=15+5+5-2-2=11,0-4+5+0+0=1

正解:

(1,1)(2,1)(3,1)(3,2)(3,3)(3,2)(2,2)(2,1)(1.1)(1,1)\to(2,1)\to(3,1)\to(3,2)\to(3,3)\to(3,2)\to(2,2)\to(2,1)\to(1.1)
5+0+542=4,0+0+5+5+0=105+0+5-4-2=4,0+0+5+5+0=10


动态规划

此时我们就举出了反例。还是老实用朴素的 dp 吧。本题可以理解为有两个人从 (1,1)(1,1) 走到 (n,n)(n,n)。那就可以将状态设为 dpx,y,xx,yydp_{x,y,xx,yy}。分别是第一个人的坐标,第二个人的坐标。
我们使两个人一起走,如果两人坐标相同,就只算一遍。这个时候的状态转移方程即让两个人各走一步:

dpx,y,xx,yy=max{dpx1,y,xx1,yy,dpx1,y,xx,yy1,dpx,y1,xx1,yy,dpx,y1,xx,yy1}dp_{x,y,xx,yy}=\max\{dp_{x-1,y,xx-1,yy},dp_{x-1,y,xx,yy-1},dp_{x,y-1,xx-1,yy},dp_{x,y-1,xx,yy-1}\}

但是时空复杂度:Θ(n4)\Theta (n^4)

优化

考虑优化,dp 优化的本质就是削减无用状态与重复转移。我们注意到,只有 x+y=xx+yyx+y=xx+yy 的状态是有意义的,因为如果不同,说明不符合两个人一起走的定义。

假如两个人同时走,那么他们的坐标和就会相同。设坐标和为 ss。那么枚举 s,x,xxs,x,xxy,yyy,yy 就可以算出来了。此时可设 dps,x,xxdp_{s,x,xx} 为坐标和为 ss,第一个人在 (x,sx)(x,s-x),第二个人在 (xx,sxx)(xx,s-xx)

接下来思考状态转移方程,其实就是由朴素版推出来的:

dps,x,xx=max{dps1,x1,xx,dps1,x,xx1,dps1,x1,xx1,dps1,x,xx}dp_{s,x,xx}=\max\{dp_{s-1,x-1,xx},dp_{s-1,x,xx-1},dp_{s-1,x-1,xx-1},dp_{s-1,x,xx}\}

复杂度就变成 Θ(n3)\Theta(n^3)


初始值

在开始的时候,我们要将所有状态设为不可达。但是 dp2,1,1dp_{2,1,1} 的状态是确定的。dp2,1,1a1,1dp_{2,1,1}\gets a_{1,1}

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 3e2+1;
int a[N][N],dp[N<<1][N][N],n;
int main() {
	memset(dp,-0x3f,sizeof dp);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			cin>>a[i][j];
		}
	}
	dp[2][1][1]=a[1][1];
	for(int i=3;i<=n<<1;i++) {
		for(int j=1;j<=n;j++) {if(i-j<1||i-j>n) continue;//枚举 x;y为 i-x+1,不能越界
			for(int k=1;k<=n;k++) {if(i-k<1||i-k>n) continue;//同上 
				int t=0;
				if(j==k) t=a[j][i-j];//相同只算一个 
				else t=a[j][i-j]+a[k][i-k];
				dp[i][j][k]=max(max(dp[i-1][j-1][k],dp[i-1][j][k-1]),max(dp[i-1][j][k],dp[i-1][j-1][k-1]))+t;
			}
		}
	}
	cout<<dp[n<<1][n][n];
	return 0;
}
posted @ 2023-08-16 21:04  cjrqwq  阅读(20)  评论(0)    收藏  举报  来源