题解 SP212 【WATER - Water among Cubes】
虽然源OJ显示是bfs,可是我却觉得是堆。。。
思路:每次找到一个在最边上的最小的,然后把那个最小的删除,如果扫描周围的鸽子是否能装水(即这个格子低于边上的那个格子),如果可以蓄水,就加入到sum里。
记得删除完这个格子把与其相邻的格子也设为边上的格子哦,但需要注意的一点是如果这个格子可以蓄水,一定要赋成那个边上的格子,因为其他的格子不是只蓄水到这个格子的高度,而是可以到边上的那个格子。
但我们怎么求最小值呢?插入排序?但由于既要查找,又要插入每次的操作是O(log2(n)+n)太慢了。
块状链表?似乎快一点,每次可以接近O(2×
所以我们只能用堆来找最小值了,每次操作 O(log2(n)),总时间复杂度O(t×n×m×log2(n))。
最后,我们要避免元素在堆里面出现多次,如果已经在堆里,就要标记一下,只有当没被标记过的时候才可以插入。
代码
#include<bits/stdc++.h>
using namespace std;
struct xyq{
int x,y,q; //q是元素的值(即装到多少高度就会溢出)
};
xyq tree[500005]; //堆。
bool mark[505][505]; //标记数组。
int size=0,a[505][505];
int gox[10]={0,0,1,-1}; //搜索方向(接着下面你也许就懂了)。
int goy[10]={1,-1,0,0};
void push(int a,int b,int c){ //插入一个节点的函数。
size++;
tree[size].q=a;
tree[size].x=b;
tree[size].y=c;
int mq=size; //mq表示目前调整到的节点。
while(tree[mq>>1].q>tree[mq].q){ //向上调整,(x>>1)是位运算,表示x/2。
swap(tree[mq>>1],tree[mq]);
mq>>=1;
}
}
xyq pop(){
xyq ret=tree[1];
int t=2; //目前调整到的节点。
tree[1]=tree[size--]; //删除不需要取消标记,因为删除了就不需要再插入。
while(t<=size){ //向上调整。
if(t<size&&tree[t].q>tree[t+1].q){
t++;
}
if(tree[t].q<tree[t>>1].q){
swap(tree[t],tree[t>>1]);
t=(t<<1); //(x<<1)是位运算,表示2x。
}else{
break;
}
}
return ret;
}
int main(){
int i,j,n,m,nx,ny,k,t; //n,m是输入的行数和列数。nx,ny是搜索到的新节点
long long sum=0;
xyq zyl;
cin>>t;
for(int it=1;it<=t;it++){
cin>>n>>m;
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
mark[i][j]=0;
}
}
sum=0; //清空所有使用记录。
for(i=0;i<n;i++){
for(j=0;j<m;j++){
cin>>a[i][j];
if(i==0||i==n-1||j==0||j==m-1){
push(a[i][j],i,j);
mark[i][j]=1;
}
}
}
for(i=0;i<n*m;i++){
zyl=pop();
for(j=0;j<4;j++){ //对方向进行搜索
nx=zyl.x+gox[j];
ny=zyl.y+goy[j];
if(nx>=0&&ny>=0&&nx<n&&ny<m&&!mark[nx][ny]){ //没超过边界并且没插入过。
sum+=max(zyl.q-a[nx][ny],0);
push(max(a[nx][ny],zyl.q),nx,ny);
mark[nx][ny]=1; //标记。
}
}
}
cout<<sum<<endl;
}
}