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

 一道新题不会写,老办法 ,分割成许多个小问题

比如这个一笔画问题;

先写出来一笔画的思路

 
 
  
  数学家欧拉找到一笔画的规律是:
  
  
  ■⒈凡是由偶点组成的连通图,一定可以一笔画成。画时可以把任一偶点为起点,最后一定能以这个点为终点画完此图。
  ■⒉凡是只有两个奇点的连通图(其余都为偶点),一定可以一笔画成。画时必须把一个奇点为起点,另一个奇点终点。
  ■⒊其他情况的图都不能一笔画出。(有偶数个奇点除以二便可算出此图需几笔画成。) 根据欧拉总结的规律,我们只需要1、判断图是否联通2、判断点是奇点的个数,就可以了。
(1)判断连通图(个人觉得,这弄好了,这道题也就完了)

方法(1)

递归,对,就是递归,递归一下,看能递归到所有数不,如果不是连通的递归会中断,有的数就不会被递归到

递归还有一个问题就是要建图,建图的目的就是保存那些连通的点,

总不能每递归一次就要从头遍历一遍找下一个点吧,那样妥妥的超时,

而我们要做的就是找一种方法,让我们能最快的找到我们想要的点,那就是建图

#include<stdio.h>
#include<string.h>
int point,line;
bool map[1005][1005];
bool vis[1005];
int a[2000],b[2000];
int dfs(int x)
{
    vis[x]=true;
    for(int j=1;j<=point;j++)//图的同一行向右找
    {
        if(map[x][j]==true)//如果找到,继续递归
        {
            dfs(j);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int flag=1;
        scanf("%d%d",&point,&line);
        memset(map,false,sizeof(map));//初始化
        memset(vis,false,sizeof(vis));
        for(int i=0;i<line;i++)
        {
            scanf("%d%d",&a[i],&b[i]);  
            map[a[i]][b[i]]=true;     //输入的同时开始建图
            map[b[i]][a[i]]=true;
        }
        dfs(a[0]);                 //开始递归
        for(int i=1;i<=point;i++)    //如果所有的点都递归过,flag=1;
        {
            if(vis[i]==false)
            {
                flag=0;
            }
        }
        if(flag==0)
        {
            printf("NO\n");
        }
        else
        {
            printf("YES\n");
        }
    }
}

初步的就出来了,但是还不行

输入样例就错了,进入了死循环

if(map[x][j]==true&&vis[j]==false)//如果找到,继续递归
好了 加一点,成了,

下面判断一下是否能一笔画完

简单说一下我对 欧拉结论的理解

首先说几个概念  (1)度; 一个点的度  指跟这个点相连的线的条数

如第一个样例,画个图,点1的度是3 其余的点的度都是1;

(2)奇点,度为1的点为奇点;

(3)偶点,度为偶数的点;

点是可以多次经过的,而边只能过一次,

打个比方,每个点是一个车站,而边是路,车每走一次这条路,路就塌陷了(豆腐渣工程)

先不看出发点,和终点;

偶数点有个特点是,对于这个车站来说,车不论经过这个车站多少次,车最后都能出去,

奇数点就不一样了,车总会被困在这个车站

如果想让车不被困在奇数点,唯一的办法就是这个奇数点是出发点,或者终点

那么,奇数点只能有两个,

所以  第一种情况就出来了

2个奇数点,其余全是偶数点,而且奇数点必须是发车点和终点

你问我为啥不会是  一个奇数点,其余全是偶数点,哈!因为不可能是这种情况啊

1条边需要两个度,边是整数,那总度(所有点度之和)肯定是偶数,这情况 总度都不是偶数

第二种情况

所有的点都是偶数,根据上面说的,偶数点的特点就是,车不会卡死在这里

那所有的点都是这样,不会卡死车,那车哪能跑不完全程那,对吧!

对于终点和出发点,必须是同一个点(而且肯定是)

这样 所有情况咱都考虑完了


总结一下;

(1) 奇数点肯定不能超过2个;

(2)奇数点为1个的情况不存在

(2)偶数可以随便有;

上面搞完数学了,该搞代码了

就计算一个各个点的度,判断一下上面的情况

#include<stdio.h>
#include<string.h>
int point,line;
bool map[1005][1005];
bool vis[1005];
int a[2005],b[2005],d[1005];
int dfs(int x)
{
    vis[x]=true;
    for(int j=1; j<=point; j++) //图的同一行向右找
    {
        if(map[x][j]==true&&vis[j]==false)//如果找到,继续递归
        {
            dfs(j);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int flag=1,num=0;
        scanf("%d%d",&point,&line);
        memset(map,false,sizeof(map));//初始化
        memset(vis,false,sizeof(vis));
        for(int i=0; i<line; i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            map[a[i]][b[i]]=true;     //输入的同时开始建图
            map[b[i]][a[i]]=true;
            d[a[i]]++;                //同时开始统计度
            d[b[i]]++;
        }
        dfs(a[0]);                   //开始递归
        for(int i=1; i<=point; i++)  //如果所有的点都递归过,flag=1;
        {
            if(vis[i]==false)
            {
                flag=0;
            }
        }
        for(int i=1; i<=point; i++)
        {
            if(d[i]%2==1)
            {
                num++;
            }
        }
        if(flag==1)
        {
            if(num==2||num==0)
            {
                printf("Yes\n");
            }
            else
            {
                printf("No\n");
            }
        }
        else
        {
            printf("No\n");
        }
    }
    return 0;
}

.....提交是WA......

下面DBUG

....忘记给数组  d  清零了

老毛病

AC代码

#include<stdio.h>
#include<string.h>
int point,line;
bool map[1005][1005];
bool vis[1005];
int a[2005],b[2005],d[1005];
int dfs(int x)
{
    vis[x]=true;
    for(int j=1; j<=point; j++) //图的同一行向右找
    {
        if(map[x][j]==true&&vis[j]==false)//如果找到,继续递归
        {
            dfs(j);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int flag=1,num=0;
        scanf("%d%d",&point,&line);
        memset(map,false,sizeof(map));//初始化
        memset(vis,false,sizeof(vis));
        memset(d,0,sizeof(d));
        for(int i=0; i<line; i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            map[a[i]][b[i]]=true;     //输入的同时开始建图
            map[b[i]][a[i]]=true;
            d[a[i]]++;                //同时开始统计度
            d[b[i]]++;
        }
        dfs(a[0]);                   //开始递归
        for(int i=1; i<=point; i++)  //如果所有的点都递归过,flag=1;
        {
            if(vis[i]==false)
            {
                flag=0;
            }
        }
        for(int i=1; i<=point; i++)
        {
            if(d[i]%2==1)
            {
                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;
}
不过这次dbug

花了不到10分钟,进步,继续坚持











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值