原题链接

一句话总结:四个关键点是“支架”。

首先注意到右下角的 $n \times n$ 区域内的雪必须全部清除,否则连目的地都不安全,一定会有人生病。

于是我们只需要在左下和右上两块区域中设法开出一条路来。

注意到有八个点,一旦清扫完成就不需要清扫别的点。这些点是 $(n+1, 1), (n+1, n), (2n, 1), (2n, n), (1, n+1), (1, 2n), (n, n+1), (n, 2n)$。感性理解一下。

事实上,只需要在这些点中取最小代价,就得到了最优答案。

因为任何一种合法答案,都必然包含这八个点中的一个。

回到开头的省流,四个关键点是 $(1, 1), (1, n), (n, 1), (n, n)$。

直观上理解,它们不能全都走别的点的路径离开,必须有一个有属于自己的路。或者说,它们支撑了整个网格的结构,如果不移走其中一个的话就动弹不得。

具体的,我们最终肯定需要挪动这四个点到右下角。那么考虑第一次操作它们所在的任意行列。不可能在不使这四个点都不超出左上角 $n \times n$ 区域的情况下进行一次操作。

那么至少有一个点要移动到有雪的区域,我们需要清理它的落点。这落点一共可能有八个,就是上面列出的那些。

所以必然清理这八个点中的至少一个。同时,清理其中任意一个都足够了,不需要额外清理其他点。所以这八个点的代价取 $\min$ 就是答案。

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>

using namespace std;
int t;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> t;
    for (int _ = 0; _ < t; ++_) {
        int n;
        cin >> n;

        int64_t term1 = 0;
        vector c(2 * n + 1, vector<int64_t>(2 * n + 1));

        for (int i = 1; i <= 2 * n; ++i) {
            for (int j = 1; j <= 2 * n; ++j) {
                cin >> c[i][j];
                if (i > n && j > n) {
                    term1 += c[i][j];
                }
            }
        }

        int64_t term2 = c[1][n + 1];
        term2 = min(term2, c[1][2 * n]);
        term2 = min(term2, c[n][n + 1]);
        term2 = min(term2, c[n][2 * n]);

        term2 = min(term2, c[n + 1][1]);
        term2 = min(term2, c[n + 1][n]);
        term2 = min(term2, c[2 * n][1]);
        term2 = min(term2, c[2 * n][n]);

        cout << term1 + term2 << '\n';
    }
    return 0;
}