题目大意:一个岛屿是一个n*m的格子,每个单位格的高度不一样,岛周围的水每年的高度不一样,给出 t 年每年水的高度,水的高度是递增的,求出在每一年中高出当年水的高度有几块连通的岛屿。
思路分析:
①可以把每个格子的高度都存起来,从到小排序,在这里我使用优先队列,其实还是直接排序好些,从最后一年的海的高度进行筛选,然后用岛的高度来筛选海的高度:
①如果岛屿的高度高于海的高度,那么对它四周的岛的高度进行判断,如果也找到高于海的高度的岛,同时它们不在一个集合中(说明它们没有连接过)把它们连接在一起,同时联通的个数减一;(注意减一这里,我再解释一下,其实就是每个高于海的岛屿都要加一次,相当于每个岛屿都是独立的情况,“联通块”也是最大的,那么我们在通过并查集联通时,相当于把两个链接在一起,“联通块”数自然要减一,而通过对当前高于海高度岛的周围进行判断合并减一时,过程中可能会出现负的联通块的值,没关系,等队列进行这一个点判断又会加回来。。。)
② 如果岛屿的高度小于海的高度,那么当前这一年的连通的块数也就求出来了,而年数就应该减一,知道找到海的高度低于岛的高度的那一年。
③用这种方法时,要注意岛屿的高度都高于第二年往上的高度,也就是在第三年第四年。。。的时候队列里的点都高于这一年海的高度,进而队列就为空,而实际上第一年等都没有进行判断,其实连通块就是一啦。。。。用海的高度来筛选岛的高度就不会出现这种情况。
代码实现:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1000005;
int n,m,t,sum,mp[1005][1005],fa[maxn],high[100005],num[100005],dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
struct Node{
int x,y,h;
Node(int _x=0,int _y=0,int _h=0):x(_x),y(_y),h(_h){}
bool operator<(const Node& rhs)const {
return h<rhs.h;
}
};
priority_queue<Node> q;
int Find(int a){
return a==fa[a]?fa[a]:fa[a]=Find(fa[a]);
}
void Merge(int x,int y){
int fx=Find(x),fy=Find(y);
if(fx!=fy){
sum--;
fa[fy]=fx;
}
}
void Solve(){
int j=t,i;
Node head,now;
while(!q.empty()){
head=q.top();
q.pop();
while(j>0&&head.h<=high[j]) num[j]=sum,j--;
if(j==0) break;
sum++;
for(i=0;i<4;i++){
now.x=head.x+dx[i];
now.y=head.y+dy[i];
if(now.x<1||now.x>n||now.y<1||now.y>m||mp[now.x][now.y]<=high[j]) continue;
Merge((head.x-1)*m+head.y,(now.x-1)*m+now.y);
}
}
if(j!=0) for(int i=j;i>0;i--) num[i]=sum;
for(i=1;i<=t;i++) printf("%d ",num[i]);
printf("\n");
}
int main(){
int c;
scanf("%d",&c);
while(c--){
scanf("%d%d",&n,&m);
while(!q.empty()) q.pop();
for(int i=1;i<=m*n;i++) fa[i]=i;
memset(num,0,sizeof(num));
sum=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&mp[i][j]);
q.push(Node(i,j,mp[i][j]));
}
}
scanf("%d",&t);
for(int i=1;i<=t;i++) scanf("%d",&high[i]);
Solve();
}
}