CF2194E题解

传送门:https://www.luogu.com.cn/problem/CF2194E

题意:方格取数,可以有一次修改,把某个格子上的数修改成相反数,求修改后最大取数结果的最小值。

首先先求出最优路径,很显然必须要在最优路径上修改才可能使最优结果变小。

接下来可以枚举最优路径上的点,依次修改成相反数再找最优解。复杂度不能忍受。只能接受若干次次 \(O(nm)\) 的dp。

考虑必经点的做法,枚举断点拆成两段路径,那我们如何把必经点转换到不经点?

一种可行的方法是可以枚举与不经点到原点哈曼顿距离相同的点,这样枚举的结果必定不会包含不经点也囊括了所有到终点的路径,我们维护一下 \(x+y\) 与 当前点相同的点作为必经点时的次大值与最优路径当前格取相反数的结果就能得到修改最优路径上某个点得到的最大可能取数结果。在所有枚举中取最小值即可。

时间复杂度 \(O(n+m)\)。

最后吐槽一下这场比赛中的 \(n,m\le\) 。。

#include <bits/stdc++.h>

#define int long long

const int N = 1e6 + 1;

using namespace std;

vector<int> a[N], dp[N], pre[N], pd[N];

int f[N][2];

void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        vector<int> t1, t2, t3, t4;
        swap(t1, a[i]);
        swap(t2, dp[i]);
        swap(t3, pre[i]);
        swap(t4, pd[i]);
        a[i].push_back(0), dp[i].push_back(0), pre[i].push_back(0), pd[i].push_back(0);
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            dp[i].push_back(0), pd[i].push_back(0), pre[i].push_back(0);
            int x;
            cin >> x;
            a[i].push_back(x);
        }
    }
    dp[1][1] = a[1][1];
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (i == 1 && j == 1) continue;
            if (i == 1) dp[i][j] = dp[i][j - 1] + a[i][j];
            else if (j == 1) dp[i][j] = dp[i - 1][j] + a[i][j], pre[i][j] = 1;
            else {
                if (dp[i][j - 1] >= dp[i - 1][j]) dp[i][j] = dp[i][j - 1] + a[i][j];
                else dp[i][j] = dp[i - 1][j] + a[i][j], pre[i][j] = 1;
            }
        }
    }
    pd[n][m] = a[n][m];
    for (int i = n; i; --i) {
        for (int j = m; j; --j) {
            if (i == n && j == m) continue;
            if (i == n) pd[i][j] = pd[i][j + 1] + a[i][j];
            else if (j == m) pd[i][j] = pd[i + 1][j] + a[i][j];
            else {
                if (pd[i][j + 1] >= pd[i + 1][j]) pd[i][j] = pd[i][j + 1] + a[i][j];
                else pd[i][j] = pd[i + 1][j] + a[i][j];
            }
        }
    }
    for (int i = 2; i <= n + m; ++i) f[i][0] = f[i][1] = -1e15;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            int val = dp[i][j] + pd[i][j] - a[i][j];
            if (val >= f[i + j][0]) f[i + j][1] = f[i + j][0], f[i + j][0] = val;
            else if (val > f[i + j][1]) f[i + j][1] = val;
        }
    }
    int ans = dp[n][m];
    int i = n, j = m;
    while (j > 0) {
        ans = min(ans, max(f[i + j][0] - 2 * a[i][j], f[i + j][1]));
        if (pre[i][j]) --i;
        else --j;
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}
posted @ 2026-05-08 07:58  Jefferyzzzz  阅读(14)  评论(0)    收藏  举报