「EZEC-4.5」走方格
Solution
考虑对于每个格子,计算经过它的最长路是多少。从 \((1,1)\) 开始正着 \(\mathrm{dp}\) 一遍,得到数组 \(\mathrm{f1[][]}\),再从 \(\mathrm{(n,m)}\) 开始反着 \(\mathrm{dp}\) 一遍,得到数组 \(\mathrm{f2[][]}\)。
对于 \(\mathrm{(i,j)}\),经过它的最长路 \(dis_{i,j}=f1_{i,j}+f2_{i,j}-a_{i,j}\)(\(a_{i,j}\) 重复计算)
由于只能向下或向右走,通过观察(口胡),可以发现一个性质:每个与 \((1,1)\) 的曼哈顿距离只会走 \(1\) 次。换成人话,就是每个左下-右上的对角线将会被经过恰好一次。同一条对角线上的点满足 \(i + j\) 相等。
这样我们考虑维护条对角线上 经过每个点最大价值 的最大值(\(\mathrm{maxx}\))和次大值(\(\mathrm{sec}\))。那么枚举 \(\mathrm{(i,j)}\) 点被置成 \(0\) 之后的最大价值是:
\[\max \left\{ sec_{i+j}, f1_{i,j} + f2_{i,j} - 2 \times a_{i,j} \}\right.
\]
\[(如果 \mathrm{dis_{i,j}} 是该对角线的最大值)
\]
\[\max \left\{ maxx_{i+j}, f1_{i,j} + f2_{i,j} - 2 \times a_{i,j} \}\right.
\]
\[(如果 \mathrm{dis_{i,j}} 不是该对角线的最大值)
\]
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 2333;
LL n, m, Ans = 1e18;
LL idx[N << 4], idy[N << 4], a[N][N], f1[N][N], f2[N][N], maxx[N << 4], sec[N << 4];
signed main()
{
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%lld", &a[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
f1[i][j] = max(f1[i - 1][j] + a[i][j], f1[i][j - 1] + a[i][j]);
for(int i = n; i >= 1; i--)
for(int j = m; j >= 1; j--)
f2[i][j] = max(f2[i + 1][j] + a[i][j], f2[i][j + 1] + a[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
LL res = f1[i][j] + f2[i][j] - a[i][j];
if(res > maxx[i + j])
{
sec[i + j] = maxx[i + j];
maxx[i + j] = res;
idx[i + j] = i, idy[i + j] = j;
}
else sec[i + j] = max(sec[i + j], res);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(idx[i + j] == i && idy[i + j] == j)
Ans = min(Ans, max(sec[i + j], f1[i][j] + f2[i][j] - 2 * a[i][j]));
else Ans = min(Ans, max(maxx[i + j], f1[i][j] + f2[i][j] - 2 * a[i][j]));
}
printf("%lld", Ans);
return 0;
}