哈理工 校赛(热身赛)2238 围巾的纠结(判断回路有关问题)

哈理工 校赛(热身赛)2238 围巾的纠结(判断回路问题)

围巾的纠结

Time Limit: 500 MS

Memory Limit: 32768 K

Total Submit: 123(52 users)

Total Accepted: 57(48 users)

Rating: 哈理工 校赛(热身赛)2238  围巾的纠结(判断回路有关问题)哈理工 校赛(热身赛)2238  围巾的纠结(判断回路有关问题)

Special Judge: No

Description

小破想要织一条围巾,可是她并不擅长于织围巾。由于工作太忙,每天她都只能织一点。为了快速的织完围巾,她决定直接把毛线球连在一起。围巾上有球球还是非常可爱的嘛~

    于是小破去商店买了n个毛线球。每天晚上睡觉之前,她就抽出一点时间,把其中两个原本分开的毛线球织在一起。由于是睡觉之前干活,人太困了,过了m天之后,她发现自己完全织乱了,于是她决定拆掉重织。可是由于她的织法过于特殊,她发现,一旦几个毛线球被织成了一个圈,就不能完好的解开了。现在她想知道,她还能把这条"围巾"还原吗?

Input

多组测试数据
每组测试数据第一行有两个整数n,m(1 <= n <= 10000, 1 <= m <= 1000000)
接下来有m行,每行有两个数a,b,表示将a和b号毛球织到一起。

Output

对于每组测试数据,如果她能还原,则输出"YES", 否则输出"NO"

Sample Input

5 4
1 2
2 3
3 1
1 4
5 4
1 2
2 3
3 4
4 5

Sample Output

NO
YES

Source

哈尔滨理工大学第五届ACM程序设计竞赛(热身)

 

 

话说 何止围巾的纠结?!!用了好几种方法都超内存,最后习得并查集判断无向图回路问题。分分秒解决~。

先来看看这种超时的经典算法:

按照算法描述写出来也是不容易啊。

#include<iostream>
#include<string.h>
#include<set>
#include<queue>
using namespace std;
const int M=10010;
int graph[M][M];
int digree[M];

int main()

{
    int n,m;
    while(cin>>n>>m,n+m)
    {
        memset(graph,0,sizeof(graph));
        memset(digree,0,sizeof(digree));
        for(int i=1; i<=m; i++)
        {
            int a,b;
            cin>>a>>b;
            if(!graph[a][b]&&!graph[b][a])//防止有重边,导致各定点度数变大。
            {
                graph[a][b]=1;添加双向边
                graph[b][a]=1;
                digree[b]++;
                digree[a]++;
            }
        }
        queue<int>q;
        set<int>dict;
        for(int j=1; j<=n; j++)
        {
            if(digree[j]==0||digree[j]==1)//因为图的子图是一个回路,那么每个顶点度数大于等于2,所以删除读数为0和1的结点
            {
                q.push(j);
            }
        }

        while(!q.empty())
        {
            int cur=q.front();
            dict.insert(cur);
            q.pop();
            for(int k=1; k<=n; k++)
            {
                if(graph[cur][k])
                {
                    graph[k][cur]=0;//删除这个结点和所在的边
                    graph[cur][k]=0;
                    digree[k]--;    //同时相连的结点度数减1
                    if(digree[k]==0||digree[k]==1)//判断是否这个顶点可以删除
                        q.push(k);
                }
            }
        }

        if(dict.size()!=n)  //如果每个顶点都进入过队列,则说明可以全部删除,反之则反,另外不能用count记录撤出队列元素个数
            cout<<"NO"<<endl;//来判断(!);
        else
            cout<<"YES"<<endl;
    }
    return 0;
}
/*
5 5
2 1
1 2
2 3
3 4
4 5
 */

如果使用并查集原理,每次往图中加入一条边之前判断两个短点是否属于不同的连通块儿,如果是,那么可以合并,否则必然存在回路。

#include<iostream>
#include<string.h>
using namespace std;
const int M=50001;
int F[M];

int Find(int x)
{
    if(x!=F[x])
        return F[x]=Find(F[x]);
}

bool Is_same(int x,int y)
{
    return Find(x)==Find(y);
}

void union_set(int x , int y)
{
    int tx=Find(x);
    int ty=Find(y);
    if(tx!=ty)
    {
        F[tx]=ty;
    }
}

int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        int flag=0;
        for(int j=0;j<M;j++)
            F[j]=j;
        for(int i=0;i<m;i++)
        {
            int a,b;
            cin>>a>>b;
            if(Is_same(a,b))
            flag=1;
            union_set(a,b);
        }
        if(flag)
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
    return 0;
}