传说中木桶效应缩圈题,吃鸡就离谱。
注意一个点只能影响他的上下左右,斜方是无效的。
把范围想象一个长方形,先读入外圈的点,给外圈标记全遍历过,进优先队列(边角四个点的上下左右点要么超了范围要么还是外圈,所以干脆不进队列)。
找到队列最小值的节点,众所周知,这个点的上下左右点中最高要么是该点自己的高度,要么是和这个最小的点一样(高了就溢出了),于是乎这些点的最大值找到了。
找到了就更新,更新了放入队列中,你会发现圈缩小了。
然后以此类推到空空状态。
PS:这里数据是不是都是正方形?之前把n写成m也过了(逃
#include <bits/stdc++.h>
using namespace std;
int h[55][55];//原始值
bool vis[55][55];//该点是否被遍历
int dir[4][2] = {{0, -1},
{1, 0},
{-1, 0},
{0, 1}};//一个点的上下左右
int ans;//答案
struct Node
{
int x, y;
int val;
Node(int a, int b, int v)
{
x = a;
y = b;
val = v;
}
friend bool operator<(Node a, Node b)//C艹的优先队列找最小值一定要大于,刚好反过来
{
return a.val > b.val;
}
};
int main()
{
int t;
int n, m;
cin >> t;
for (int cnt = 0; cnt < t; cnt++)
{
cin >> n >> m;
//清空
ans = 0;
memset(vis, false, sizeof(vis));
priority_queue<Node> pq;
//读入
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> h[i][j];
//外圈中横着的点(不包括四个角)
for (int i = 1; i < m - 1; i++)
{
vis[0][i] = vis[n - 1][i] = true;
pq.push(Node(0, i, h[0][i]));
pq.push(Node(n - 1, i, h[n - 1][i]));
}
//外圈中竖着的点(不包括四个角)
for (int i = 1; i < n - 1; i++)
{
vis[i][0] = vis[i][m - 1] = true;
pq.push(Node(i, 0, h[i][0]));
pq.push(Node(i, m - 1, h[i][m - 1]));
}
//边角四个点记得还是要标记已经遍历
vis[0][0] = vis[0][m - 1] = vis[n - 1][0] = vis[n - 1][m - 1] = true;
//BFS
while (!pq.empty())
{
Node temp = pq.top();
pq.pop();
vis[temp.x][temp.y] = true;
for (int i = 0; i < 4; i++)//上下左右的点
{
int tx = temp.x + dir[i][0];
int ty = temp.y + dir[i][1];
if (!(tx < n && tx >= 0 && ty < m && ty >= 0) || vis[tx][ty])//如果超范围或者被遍历过了就pass
continue;
vis[tx][ty] = true;
ans += max(0, temp.val - h[tx][ty]);//ans加上该点的木桶高度和它高度的差,如果大于0就是积水量,因为总会出现上下左右的点高于temp的情况,那就没有积水
pq.push(Node(tx, ty, max(temp.val, h[tx][ty])));//注意,push的是上下左右点的最大高度,如果木桶高度小于它高度,仍然要它本身的高度,因为它是新边界,而边界高度就是木桶高度和原本高度的最大值
}
}
printf("Case #%d: %d\n", cnt + 1, ans);
}
return 0;
}
牛啊%%%
牛皮
y总的没看懂 你的看懂了 👍