CF2194E The Turtle Strikes Back 题解
很显然修改一个点的数值之后,最优路径有两种可能:最优路径经过这个点和不经过这个点。
- 经过最优点我们可以用从\((1,1)\)向右下转移和从\((n,m)\)向左上转移,求出从\((1,1)\)到\((i,j)\)和从\((i,j)\)到\((n,m)\)的最优路径,然后拼起来再减掉\(3a_{i,j}\)就可以,因为原来的dp值相加会多出来一个\(a_{i,j}\),而\(a_{i,j}\)变为相反数之后,是要减掉\(3a_{i,j}\)的。这个分别从首尾dp两次的方法和https://codeforces.com/contest/2195/problem/F 这道题的dp方法很像。
- 如果不经过这个点的话,我们可以通过强制选一些点使得他们的路径当中不会经过\((i,j)\)这个点。如图所示,为了不经过\((i,j)\)这个红色的点,我们只需要从图中涂蓝色点中找出最大的dp值即可,即对于\((i,j)\)这个点,只需要求出\((i,k),1\le k\le j-1\)和\((k,j),1\le k \le i-1\)中最大的dp值。因为每次行动只可以向右或者向下走,所以说从这些点一定是所有不经过\((i,j)\)这个点的路径中必然会经过的点的集合。快速求出最大值我们需要对每一行和每一列都维护前缀dp最大值。
、![image]()
点击查看代码
#include <bits/stdc++.h>
using namespace std;
/*
*/
#define ll long long
#define pii pair<int,int>
void out(){
cout<<"hel\n";
}
void sol() {
int n,m;
cin>>n>>m;
vector<vector<ll>> a(n+1,vector<ll>(m+1,-1e18)),fz,fn;
vector<vector<pii>> fx(n+1,vector<pii>(m+1)),fy;
fz=fn=a;
fy=fx;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
fz[1][1]=a[1][1],fn[n][m]=a[n][m];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i*j==1ll) continue;
fz[i][j]=max({fz[i-1][j],fz[i][j-1]});
fz[i][j]+=a[i][j];
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
if(i==n&&j==m) continue;
if(i+1<=n) fn[i][j]=max(fn[i][j],fn[i+1][j]);
if(j+1<=m) fn[i][j]=max(fn[i][j],fn[i][j+1]);
fn[i][j]+=a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j==1) fx[i][j]={i,j};
else{
auto [x,y]=fx[i][j-1];
if(fz[i][j]+fn[i][j]-a[i][j]>fz[x][y]+fn[x][y]-a[x][y]) fx[i][j]={i,j};
else fx[i][j]={x,y};
}
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
if(i==1) fy[i][j]={i,j};
else{
auto [x,y]=fy[i-1][j];
if(fz[i][j]+fn[i][j]-a[i][j]>fz[x][y]+fn[x][y]-a[x][y]) fy[i][j]={i,j};
else fy[i][j]={x,y};
}
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=m;j++){
// cout<<fz[i][j]<<" "<<fn[i][j]<<'\n';
// }
// }
ll ans=1e18;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ll res=-1e18;
if(i+1<=n&&j>=2){
auto&[x,y]=fx[i+1][j-1];
res=max(res,fz[x][y]+fn[x][y]-a[x][y]);
}
if(j+1<=m&&i>=2){
auto&[x,y]=fy[i-1][j+1];
res=max(res,fz[x][y]+fn[x][y]-a[x][y]);
}
res=max(res,fz[i][j]+fn[i][j]-3*a[i][j]);
ans=min(ans,res);
}
}
cout<<ans<<'\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
sol();
}
return 0;
}
这里还有另一种写法,在这个只能向右或者向下的网格当中,任意一条路径上再如图所示的每一条斜线当中一定是有且仅有一个格子是该路径当中的一个的格子。所以只需要维护每个对角线上的前两优路径即可。

总结:
- 复杂情况注意分类讨论
- 求强制选定某个点的路径的时候,可以分成起点到该点,该点到终点两部分分别进行动态规划
- 注意只能向右或向下走的格子的性质


浙公网安备 33010602011771号