题目
题目链接:https://www.luogu.com.cn/problem/P6855
有 \(n\times m\) 的方格矩阵,小 A 从 \((1,1)\) 出发到 \((n,m)\) ,只能向下或向右走,获得的分数为他经过方格的权值之和。
已知每个方格 $(i,j) $的权值 \(a_{i,j}\),你可以将其中任意一个方格上的权值变为 \(0\),求变化后小 A 最多能获得分数的最小值。
思路
首先我们发现,改动为 \(0\) 的格子一定在最长路径上,否则改了这个格子最长路径长度没变,显然不是最优解。
因为从点 \((1,1)\) 到 \((n,m)\) 且只可以往下或往右的任意一条路径长度都为 \(n+m-1\),所以答案只可能是修改这 \(n+m-1\) 个点中的一个。
我们先求出 \(f[i][j]\) 表示点 \((1,1)\) 到 \((i,j)\) 的最长路径长度,\(g[i][j]\) 表示点 \((n,m)\) 到 \(i,j\) 的最长路径长度,然后枚举最长路径上的每一个点 \((x,y)\),需要求出将这个点权值设为 \(0\) 之后从 \((1,1)\) 到 \((n,m)\) 的最长路径。
经过点 \((x,y)\) 的最长路径长度为 \(f[x][y]+g[x][y]-2\times a[x][y]\),对于不经过点 \((x,y)\) 的点,我们枚举这条路径从第 \(x\) 行走向第 \(x+1\) 行是在哪一列,设为 \(k\),那么设 \(h[k]\) 表示这条路径从第 \(x\) 行到第 \(x+1\) 行走的是第 \(k\) 列,且不经过 \((x,y)\) 的最长路径。
那么有
此时从第 \(k\) 列下去的路径长度最大值就是 \(h[k]+g[x+1][k]\)。
所以将 \((x,y)\) 赋值为 \(0\),路径长度最大值就是
在所有答案中取 \(\min\) 即可。
由于我们只尝试将 \(n+m-1\) 个点赋值为 \(0\),每个点计算复杂度是 \(O(n)\),总时间复杂度 \(O(n^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2010;
const ll Inf=1000000000000000000LL;
int n,m;
ll ans,f[N][N],g[N][N],h[N],a[N][N];
int main()
{
memset(f,0xcf,sizeof(f));
memset(g,0xcf,sizeof(g));
scanf("%d%d",&n,&m);
ans=Inf; f[0][1]=f[1][0]=g[n+1][m]=g[n][m+1]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%lld",&a[i][j]);
f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];
}
for (int i=n;i>=1;i--)
for (int j=m;j>=1;j--)
g[i][j]=max(g[i+1][j],g[i][j+1])+a[i][j];
for (int x=n,y=m;x!=1 || y!=1;)
{
ll maxn=0;
h[0]=-Inf;
for (int i=1;i<=m;i++)
{
if (i==y) h[i]=-Inf;
else
{
h[i]=max(h[i-1],f[x-1][i])+a[x][i];
maxn=max(maxn,h[i]+g[x+1][i]);
}
}
maxn=max(maxn,f[x][y]+g[x][y]-a[x][y]*2LL);
ans=min(ans,maxn);
if (x==1) y--;
else if (y==1) x--;
else if (f[x-1][y]>f[x][y-1]) x--;
else y--;
}
printf("%lld",min(ans,f[n][m]-a[1][1]));
return 0;
}