Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
For a fixed sauced cell , every path splits into two camps: paths that use , and paths that dodge . Handle those two maximums separately.
The best path that uses cell is easy with ordinary max-path DP from both ends:
After flipping that cell, this becomes .
The annoying part is the best path that avoids . A path can avoid a cell only by passing through some other cell on the same anti-diagonal .
Every monotone path visits exactly one cell on each anti-diagonal. So for one diagonal, store all values and use prefix/suffix maxima to get the best value excluding the current cell.
For each non-negative cell , compute
Then minimize this. Flipping a negative cell only makes things better for Michelangelo, which is not exactly Raphael's master plan.
For a fixed cell , after Raphael flips it, Michelangelo's best path is the maximum of two kinds of paths:
The first kind is standard. Let
and
Then the best original path forced to pass through is
If Raphael flips this cell, every such path loses , so the best path using it becomes
Now we need the best path that avoids .
A monotone path from top-left to bottom-right visits exactly one cell on every anti-diagonal . So if a path avoids , then on anti-diagonal it must pass through some other cell. Conversely, if it passes through another cell on that same anti-diagonal, it cannot also pass through .
So the best path avoiding is just the best complete path through any other cell on the same anti-diagonal:
For each anti-diagonal, compute all values on it. To get “best except this cell” quickly, use prefix and suffix maxima along that diagonal. For position inside the diagonal list:
If there is no other cell on that diagonal, then avoiding this cell is impossible, so . This matters for a grid: Michelangelo must eat the only slice.
For a candidate sauced cell , the resulting best pleasure is
Raphael wants the minimum candidate.
We only need to consider cells with . If , flipping it makes that cell larger, so every path becomes no smaller than before. Since the statement guarantees at least one non-negative cell, choosing a negative cell is never optimal.
Correctness proof:
First, for any cell , is the maximum original sum of a full path that passes through . Any such path is a best prefix ending at plus a best suffix starting at , with counted twice, so subtracting it once gives exactly the best full path through .
After flipping , every path through has its sum changed by . Therefore the best flipped path among paths through is .
Next, every valid path visits exactly one cell on each anti-diagonal , because each move increases by exactly . A path avoids if and only if, on 's anti-diagonal, it uses a different cell. For any other cell on that anti-diagonal, the best path through has value and certainly avoids . Thus the best path avoiding is the maximum over all other cells on the same anti-diagonal.
So after flipping , Michelangelo's optimal value is exactly
The algorithm evaluates this exact expression for every relevant cell, so it returns the value Raphael can guarantee.
Complexity: both DP passes are , and every cell is processed once more inside its anti-diagonal. Memory is and total time is per test case.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
const ll NEG = -(1LL << 62);
while (T--) {
int n, m;
cin >> n >> m;
int total = n * m;
vector<ll> a(total), pref(total), suf(total), through(total);
auto id = [m](int i, int j) { return i * m + j; };
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) cin >> a[id(i, j)];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
ll best = NEG;
if (i == 0 && j == 0) best = 0;
if (i > 0) best = max(best, pref[id(i - 1, j)]);
if (j > 0) best = max(best, pref[id(i, j - 1)]);
pref[id(i, j)] = best + a[id(i, j)];
}
}
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
ll best = NEG;
if (i == n - 1 && j == m - 1) best = 0;
if (i + 1 < n) best = max(best, suf[id(i + 1, j)]);
if (j + 1 < m) best = max(best, suf[id(i, j + 1)]);
suf[id(i, j)] = best + a[id(i, j)];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v = id(i, j);
through[v] = pref[v] + suf[v] - a[v];
}
}
ll ans = (1LL << 62);
int maxDiag = n + m - 2;
for (int s = 0; s <= maxDiag; s++) {
int r1 = max(0, s - (m - 1));
int r2 = min(n - 1, s);
int len = r2 - r1 + 1;
vector<ll> pre(len), suff(len);
for (int k = 0; k < len; k++) {
int i = r1 + k;
int j = s - i;
ll val = through[id(i, j)];
pre[k] = (k == 0 ? val : max(pre[k - 1], val));
}
for (int k = len - 1; k >= 0; k--) {
int i = r1 + k;
int j = s - i;
ll val = through[id(i, j)];
suff[k] = (k + 1 == len ? val : max(suff[k + 1], val));
}
for (int k = 0; k < len; k++) {
int i = r1 + k;
int j = s - i;
int v = id(i, j);
if (a[v] < 0) continue;
ll avoid = NEG;
if (k > 0) avoid = max(avoid, pre[k - 1]);
if (k + 1 < len) avoid = max(avoid, suff[k + 1]);
ll use = through[v] - 2 * a[v];
ans = min(ans, max(avoid, use));
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
const ll NEG = -(1LL << 62);
while (T--) {
int n, m;
cin >> n >> m;
int total = n * m;
vector<ll> a(total), pref(total), suf(total), through(total);
auto id = [m](int i, int j) { return i * m + j; };
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) cin >> a[id(i, j)];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
ll best = NEG;
if (i == 0 && j == 0) best = 0;
if (i > 0) best = max(best, pref[id(i - 1, j)]);
if (j > 0) best = max(best, pref[id(i, j - 1)]);
pref[id(i, j)] = best + a[id(i, j)];
}
}
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
ll best = NEG;
if (i == n - 1 && j == m - 1) best = 0;
if (i + 1 < n) best = max(best, suf[id(i + 1, j)]);
if (j + 1 < m) best = max(best, suf[id(i, j + 1)]);
suf[id(i, j)] = best + a[id(i, j)];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v = id(i, j);
through[v] = pref[v] + suf[v] - a[v];
}
}
ll ans = (1LL << 62);
int maxDiag = n + m - 2;
for (int s = 0; s <= maxDiag; s++) {
int r1 = max(0, s - (m - 1));
int r2 = min(n - 1, s);
int len = r2 - r1 + 1;
vector<ll> pre(len), suff(len);
for (int k = 0; k < len; k++) {
int i = r1 + k;
int j = s - i;
ll val = through[id(i, j)];
pre[k] = (k == 0 ? val : max(pre[k - 1], val));
}
for (int k = len - 1; k >= 0; k--) {
int i = r1 + k;
int j = s - i;
ll val = through[id(i, j)];
suff[k] = (k + 1 == len ? val : max(suff[k + 1], val));
}
for (int k = 0; k < len; k++) {
int i = r1 + k;
int j = s - i;
int v = id(i, j);
if (a[v] < 0) continue;
ll avoid = NEG;
if (k > 0) avoid = max(avoid, pre[k - 1]);
if (k + 1 < len) avoid = max(avoid, suff[k + 1]);
ll use = through[v] - 2 * a[v];
ans = min(ans, max(avoid, use));
}
}
cout << ans << '\n';
}
return 0;
}