算法
(Prim算法) $O(RClogRC)$
首先,根据木桶原理,木桶可以储水的最大值取决于周边最短的那一条木板。所以我们需要找的实际上是从最外层虚拟源点到矩阵中所有点的minimax path,即最大值最小的路径,只要知道这个最大值最小的权重,我们就可以减掉当前点的高度。假设我们可以放更多水,那这些水一定会沿着那条minimax path流失。而最小生成树的性质之一,就是最小生成树上每两个点之间的路径,一定满足最大值最小的性质。
所以这个题目等价于找一棵全图的最小生成树。需要外面加一个超级源点(虚拟源点),和矩阵四周所有点相连,这个点就是海平面对应的那个点。实际操作中,可以把所有边界上的点都放进堆里面。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int dx[4]{0, 1, 0, -1}, dy[4]{1, 0, -1, 0};
int r, c, H[55][55];
bool st[55][55];
struct Node {
int d, i, j;
Node(int d, int i, int j) : d(d), i(i), j(j) {
}
bool operator< (const Node& t) const {
return d > t.d;
}
};
inline bool valid(int x, int y) {
return x >= 0 && x < r && y >= 0 && y < c;
}
int prim() {
memset(st, 0, sizeof st);
if (r == 1 || c == 1) return 0;
int res = 0, curMax = 0;
priority_queue<Node> pq;
for (int i = 0; i < r; ++i) {
pq.emplace(H[i][0], i, 0);
st[i][0] = true;
pq.emplace(H[i][c - 1], i, c - 1);
st[i][c - 1] = true;
}
for (int i = 1; i < c - 1; ++i) {
pq.emplace(H[0][i], 0, i);
st[0][i] = true;
pq.emplace(H[r - 1][i], r - 1, i);
st[r - 1][i] = true;
}
while (!pq.empty()) {
auto [d, x, y] = pq.top();
pq.pop();
curMax = max(d, curMax);
res += curMax - d;
for (int i = 0; i < 4; ++i) {
int nx = x + dx[i], ny = y + dy[i];
if (valid(nx, ny) && !st[nx][ny]) {
pq.emplace(H[nx][ny], nx, ny);
st[nx][ny] = true;
}
}
}
return res;
}
int main() {
int T;
cin >> T;
for (int t = 1; t <= T; ++t) {
cin >> r >> c;
for (int i = 0; i < r; ++i) {
for (int j = 0; j < c; ++j) {
cin >> H[i][j];
}
}
cout << "Case #" << t << ": " << prim() << endl;
}
return 0;
}