洛谷 P6121 [USACO16OPEN]Closing the Farm G
题意:
这个农场一共有被用 M条双向道路连接的 N个谷仓,题目会先输入M条双向的边,再输入这N个谷仓的关闭顺序,判断N次关闭后是否是连通图。
做法:
这题用并查集和链表两个数据结构,链表用来存各个点的边,并查集是用来判断所有点是不是连通的。如果一开始就存好了整个图,边删除边边判断是否连通的话比较难,我们可以用倒着来的方法来判断,题目就变成了边加点边判断,就会简单许多。
并查集
并查集目前我会的就是两个函数和两个数组,father[x]表示x的父节点是什么,不知道的可以看书,这里不详细介绍了。size[x]表示这个连通块的节点数量。find(x)就是求x所处的这棵树的根节点,如果x和y用find(x)后的值是一样的,那么他们就处于同一个连通块中,还有一个merge(x,y)函数,就是把x和y这两个树有一个边,那么就把他们结合,形成一个连通块,详细做法可以参考我的程序(写的不是很好,也是个新手)。
CODE
#include<bits/stdc++.h> using namespace std; int tot,head[200005],to[400010],ne[400010]; int d[200005]; int father[200005],size[200005]; int ok[200005]; void jiabian(int x,int y) { ++tot; to[tot]=y; ne[tot]=head[x]; head[x]=tot; }//链表存边,刚学会,用的还是模板 int find(int x){return x==father[x]?x: father[x]=find(father[x]);}//find函数,用到了路径压缩,就是在寻找根节点的过程中将这个树的高度减少 int merge(int x, int y) { x=find(x); y=find(y); if(x==y) return 0;//先寻找两个节点的根节点,如果一样,那么就处于同一个连通块,就不用减少连通块的总数了 if(size[x] < size[y]) father[x]=y,size[y]+=size[x]; else father[y]=x,size[x]+=size[y]; return 1; }//结合 int main() { int n,m; scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=m;++i) scanf("%d%d",&a,&b),jiabian(a,b),jiabian(b,a);//双向的 for(int i=1;i<=n;++i) scanf("%d",&d[i]); int k=0;//记录当前连通块的数量 for(int i=n;i>=1;--i) { int x=d[i]; father[x]=x; size[x]=1; ++k; for(int j=head[x];j;j=ne[j]) { int y=to[j]; if(!father[y]) continue;//如果这个点还没有被加入,就直接跳过 k -= merge(x, y); } if(k<=1) ok[i]=1; } for(int i=1;i<=n;++i) puts(ok[i]? "YES":"NO"); return 0; }

浙公网安备 33010602011771号