原题链接
一句话总结:四个关键点是“支架”。
首先注意到右下角的 $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;
}
|