CV, ORL
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55; //矩阵边长
int n, m;
int h[N][N]; // 高度
int dist[N][N]; // dijstra 需要一个全局的dist
bool st[N][N]; // 判常数组
struct Node{ // 矩阵中每个点的值
int x, y, d; //坐标 和 值
// 堆里面需要所有元素进行排序
bool operator < (const Node& t) const{
return d > t.d; // 堆默认是大根堆,这题最短路,变成小根堆。所以 >
}
};
int main()
{
int T;
cin >> T; // 测试数据数量
for(int C=1; C<=T; C++) // 每个测试数据
{
cin >> n >> m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin >> h[i][j]; //每个元素的高度
// 初始化
memset(dist, 0x3f, sizeof dist); // 因为求最短路,所以先初始化为正无穷
memset(st, 0, sizeof st);
priority_queue<Node> heap;
// 把周围一圈的点加入堆里
for(int i=1; i<=n; i++)
{
heap.push({i, 1, h[i][1]}); // 先最左侧,高度为h[i][1]
dist[i][1] = h[i][1];
heap.push({i, m, h[i][m]}); // 最右侧加进来
dist[i][m] = h[i][m];
}
for(int i=2; i<m; i++) // 上下两行加进来
{
heap.push({1, i, h[1][i]});
dist[1][i] = h[1][i];
heap.push({n, i, h[n][i]});
dist[n][i] = h[n][i];
}
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int res = 0; //记录最终的储水量
while(heap.size()) //堆不空
{
auto t = heap.top();
heap.pop();
if(st[t.x][t.y]) continue; // 若该点搜过了就继续
st[t.x][t.y] = true; // 标记为它已经更新过其它点了
res += t.d - h[t.x][t.y]; // 格子的最终高度 - 其初始身的高度 即为 该格子储水量
// 枚举当前点的临边,去更新四条临边
for(int i=0; i<4; i++)
{
int x = t.x + dx[i], y=t.y + dy[i];
if(x >= 1 && x <=n && y>=1 && y<=m)
{
if(dist[x][y] > max(t.d, h[x][y])) // 如果能更新这四条临边
{
dist[x][y] = max(t.d, h[x][y]);
heap.push({x, y, dist[x][y]});
}
}
}
}
printf("Case #%d: %d\n", C, res);
}
return 0;
}