nyoj 一笔画(并查集)

一笔画问题

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 4
描述

zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来。

规定,所有的边都只能画一次,不能重复画。

 

输入
第一行只有一个正整数N(N<=10)表示测试数据的组数。
每组测试数据的第一行有两个正整数P,Q(P<=1000,Q<=2000),分别表示这个画中有多少个顶点和多少条连线。(点的编号从1到P)
随后的Q行,每行有两个正整数A,B(0<A,B<P),表示编号为A和B的两点之间有连线。
输出
如果存在符合条件的连线,则输出"Yes",
如果不存在符合条件的连线,输出"No"。
样例输入
2
4 3
1 2
1 3
1 4
4 5
1 2
2 3
1 3
1 4
3 4
样例输出
No
Yes

上篇用的是深搜,但是这一笔画不用并查集做就不叫做过这一笔画

而且建图的话 数组开的太大,数组的利用率也不高,如果数据较大,那不敢相信;

还是要用一下并查集了,而且这是学习并查集的好机会


简单的并查集

开一个数组,然后将数组初始化

初始化就是将数组里的值改为下标的值

如  a1 里面的值为1  a2  的值为2;

然后 当后面输入  路径(两点连接情况)时,

如果两个点相连,将数组里面的数变为同一个数

如  1 - 3  

那么把  a1   和a3  里面的数变为 1  (3也可以,不过后面的都要这样变)

再来一组  1- 4   a1  a4都要变成1(或者3)

数组里面数相同的表示同一个数

  如果来一组    2 - 6 那么 a2  与  a6 都为 2  (或者为6)

这样  目前有两组了;

下面先具体实现用此方法的 判断连通图


#include<stdio.h>
int point,line;
int a[1005],m[1005],n[1005];
int bh(int a,int b)
{
    if(a>=b)
    {
        return b;
    }
    else
    {
        return a;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int flag=1;
        scanf("%d%d",&point,&line);
        for(int i=1; i<=point; i++) //这里的i=1 i<=point 不可以换成i=0 i<point
        {
            a[i]=i;
        }
        for(int i=0; i<line; i++) //输入的同时开始合并一些集合
        {
            scanf("%d%d",&m[i],&n[i]);
            a[m[i]]=bh(a[m[i]],a[n[i]]);
            a[n[i]]=bh(a[m[i]],a[n[i]]);
        }
        for(int i=1; i<point; i++) //同上 不可以换
        {
            if(a[i]!=a[i+1])
            {
                flag=0;
                break;
            }
        }
        if(flag==0)
        {
            printf("NO\n");
        }
        else
        {
            printf("YES\n");
        }
    }
}

这样一个判断是否为连通图的代码就来了

如果是  输出YES  否则NO

但是,刚开始没发现,后来dbug的时候发现了

这样判断连通图是有错误的

6 6

2 3

3 4

1 2

6 5

4 5

1 6

这组数据是不对的,因为我每一次合并两个集合时只是把这个集合的一个数拉过来

其余的并没有考虑  下面修改代码

下面直接贴上AC代码

#include<stdio.h>
#include<string.h>
int a[1005],d[1005],m[2005],n[2005];
int min(int a,int b)
{
    if(a>=b)
    {
        return b;
    }
    else
    {
        return a;
    }
}
int max(int a,int b)
{
    if(a<=b)
    {
        return b;
    }
    else
    {
        return a;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int point,line,x1,x2;
        int flag=1,num=0;
        scanf("%d%d",&point,&line);
        memset(d,0,sizeof(d));
        memset(a,0,sizeof(a));
        for(int i=1; i<=point; i++)   //初始化
        {
            a[i]=i;
        }
        for(int i=1; i<=line; i++) //输入的同时开始合并一些集合
        {
            scanf("%d%d",&m[i],&n[i]);
            x1=max(a[m[i]],a[n[i]]);
            x2=min(a[m[i]],a[n[i]]);
            //printf("%d %d\n",x1,x2);
            for(int j=1;j<=point ;j++)
            {
                if(a[j]==x1)
                {
                    a[j]=x2;
                }
            }
            d[m[i]]++;
            d[n[i]]++;
            /*for(int j=1;j<=point ;j++)
            {
                printf("%d ",a[j]);
            }
            printf("\n");*/
        }
        for(int i=1; i<point; i++) //判断是否为连通图
        {
            if(a[i]!=a[i+1])
            {
                flag=0;
                break;
            }
        }
        for(int i=1; i<=point; i++)
        {
            if(d[i]%2==1)
            {
                num++;
            }
        }
        //printf("%d\n",num);
        if(flag==1&&point>1)
        {
            if(num==2||num==0)
            {
                printf("Yes\n");
            }
            else
            {
                printf("No\n");
            }
        }
        else if(point==1)
        {
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
    }
    return 0;
}

错误之处就是修改时  要从头遍历一遍,如果符合修改条件  那就一块修改了,不能只修改一个

  

啊啊啊,这并查集我怎么觉得比递归还时间长

每输入一个就要从头便利一遍,是不是我方法错误的

下一篇文章我再来一遍这道题,并查集优化法


这篇比较惨





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值