洛谷 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;
 } 

 

posted @ 2022-02-13 10:55  LikC1606  阅读(94)  评论(0)    收藏  举报