Islands
改变而来,所以可以利用上一次的数据
为简化计算从而使每个节点只计算一次,可以首先进行排序。
先建立结构体存放节点数据 行r 列 c 以及 高度 h 读入数据的同时进行初始化
每个节点的父亲初始化为自身
按照高度h对节点进行排序
然后 按照水位逆序查询,首先求ans[i](i为所求最高水位)的子集个数
过程如下 从islans最高楼房高度开始 每次读入一个高度高于 ans[i]的楼房就ans[i]++
后面对楼房的四邻进行查询,如果高度仍高于ans[i] 且与其不是同一子集就ans[i]-- 并且进行合并
这样的话 就避免了重复比如一个子集有3个相邻(111模式)的楼房构成 然后每次读入都加了1 共计3
然后在读入左边1的时候(查询四周)中间的1并入子集 并ans[i]-- 再读入中间1的时候由于与左边的1已经属于同一集合
所以不用减1 而右面的1并入集合 ans[i]-- 然后再读入右边1的时候不会出现--情况因为三者都是同一集合了
最后得出正确答案 1个集合
其余情况 同理可得 每次低水位ans[--i]利用高水位ans[i]时候的数据再继续读入楼房高度(高于低水位的数据不用再次读入)因为再次读入结果仍会是ans[i];
最后得出结论;
说的不是很明白 ,较难表达 望见谅!
时间复杂度:集合的合并算法很简单,只要将两棵树的根结点相连即可,这步操作只要O(1)时间复杂度
采用路径压缩之后,查找的时间复杂度也降低为了O(1) 这个题目还进行了排序 额O(n^2)?
那么总体就是O(n^2+1)
空间复杂度 O(n)
其实并查集的那个findFather操作就是一个递归的很简单的,不用想的太多的。
这一题利用逆向思维+并查集 水位是连续的自然数
显然题目是要求在不同水位情况下的不同子集个数 显然在水位降低的时候子集的个数是有上一个水位的情况改变而来,所以可以利用上一次的数据
为简化计算从而使每个节点只计算一次,可以首先进行排序。
先建立结构体存放节点数据 行r 列 c 以及 高度 h 读入数据的同时进行初始化
每个节点的父亲初始化为自身
按照高度h对节点进行排序
然后 按照水位逆序查询,首先求ans[i](i为所求最高水位)的子集个数
过程如下 从islans最高楼房高度开始 每次读入一个高度高于 ans[i]的楼房就ans[i]++
后面对楼房的四邻进行查询,如果高度仍高于ans[i] 且与其不是同一子集就ans[i]-- 并且进行合并
这样的话 就避免了重复比如一个子集有3个相邻(111模式)的楼房构成 然后每次读入都加了1 共计3
然后在读入左边1的时候(查询四周)中间的1并入子集 并ans[i]-- 再读入中间1的时候由于与左边的1已经属于同一集合
所以不用减1 而右面的1并入集合 ans[i]-- 然后再读入右边1的时候不会出现--情况因为三者都是同一集合了
最后得出正确答案 1个集合
其余情况 同理可得 每次低水位ans[--i]利用高水位ans[i]时候的数据再继续读入楼房高度(高于低水位的数据不用再次读入)因为再次读入结果仍会是ans[i];
最后得出结论;
说的不是很明白 ,较难表达 望见谅!
时间复杂度:集合的合并算法很简单,只要将两棵树的根结点相连即可,这步操作只要O(1)时间复杂度
采用路径压缩之后,查找的时间复杂度也降低为了O(1) 这个题目还进行了排序 额O(n^2)?
那么总体就是O(n^2+1)
空间复杂度 O(n)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 1000010
using namespace std; //只有在添加了名字空间之后才能真正调用 sort函数
int d[4][2]={0,1,0,-1,-1,0,1,0}; // 按照上下左右的方向进行搜素 以判别出是否属于同一集合
int T,n,m,i,j,k,s;
struct node
{
int r,c;
int h;
}num[MAX];
int father[MAX],h[MAX],ans[MAX],que[MAX];
bool cmp(node a,node b)
{
return a.h<b.h;
}
int findFather(int id)
{
if(id!=father[id]) father[id] = findFather(father[id]);
return father[id];
}
void unio(int fa,int fb)
{
father[fb] = fa;
}
bool isInMap(int r,int c)
{
if(r>0&&c>0&&r<=n&&c<=m) return true;
else
return false;
}
void deal()
{
for(i=n*m,j=k;j>=1;j--)//i代表总结点个数(已经排序)
{
ans[j]=ans[j+1]; //j代表水位
for(;que[j]<num[i].h;i--)
{
ans[j]++;
int t = (num[i].r-1)*m+num[i].c;
int fa = findFather(t);
// printf("%d\n",fa);
for(s=0;s<4;s++)
{
int tempr = num[i].r + d[s][1];
int tempc = num[i].c + d[s][0];
if(isInMap(tempr,tempc))
{
int id = (tempr-1)*m+tempc;
int fb = findFather(id);
if(fa == fb||h[id]<=que[j]) continue;
else
{
unio(fa,fb);
ans[j]--;
}
}
}
}
}
for(i=1;i<=k;i++)
printf("%d ",ans[i]);
printf("\n");
}
int main()
{
// freopen("1.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
int t = (i-1)*m+j;
//printf("%d",t);
scanf("%d",&h[t]);
num[t].r = i;
num[t].c = j;
num[t].h = h[t];
father[t] = t;
}
sort(num+1,num+n*m+1,cmp);
scanf("%d",&k);
for(i=1;i<=k;i++)
{
scanf("%d",&que[i]);
}
memset(ans,0,sizeof(ans));
deal();
}
return 0;
}
其实并查集的那个findFather操作就是一个递归的很简单的,不用想的太多的。