一笔画


参考资料:http://zh.wikipedia.org/wiki/%E4%B8%80%E7%AC%94%E7%94%BB%E9%97%AE%E9%A2%98

一笔画问题是柯尼斯堡问题经抽象化后的推广,是图遍历问题的一种。在柯尼斯堡问题中,如果将桥所连接的地区视为点,将每座桥视为一条边,那么问题将变成:对于一个有着四个顶点和七条连通图 G(S,E),能否找到一个恰好包含了所有的边,并且没有重复的路径。欧拉将这个问题推广为:对于一个给定的连通图,怎样判断是否存在着一个恰好包含了所有的边,并且没有重复的路径?这就是一笔画问题。用图论的术语来说,就是判断这个图是否是一个能够遍历完所有的边而没有重复。这样的图现称为欧拉图。这时遍历的路径称作欧拉路径(一个或者一条链),如果路径闭合(一个圈),则称为欧拉回路[1]

一笔画问题的推广是多笔画问题,即对于不能一笔画的图,探讨最少能用多少笔来画成

连通的无向图 G 有欧拉路径的充要条件是:G中奇顶点(连接的边数量为奇数的顶点)的数目等于0或者2。

连通的无向图 G 是欧拉(存在欧拉回路)的充要条件是:G中每个顶点的度都是偶数。[2]

证明[2][3]

  • 必要性:如果一个图能一笔画成,那么对每一个顶点,要么路径中“进入”这个点的边数等于“离开”这个点的边数:这时点的偶数。要么两者相差一:这时这个点必然是起点或终点之一。注意到有起点就必然有终点,因此奇顶点的数目要么是0,要么是2。
  • 充分性:
    1. 如果图中没有奇顶点,那么随便选一个点出发,连一个环C_{1}。如果这个环就是原图,那么结束。如果不是,那么由于原图是连通的,C_{1} 和原图的其它部分必然有公共顶点 s_{1}。从这一点出发,在原图的剩余部分中重复上述步骤。由于原图是连通图,经过若干步后,全图被分为一些环。由于两个相连的环就是一个环,原来的图也就是一个欧拉环了。
    2. 如果图中有两个奇顶点 u 和 v,那么加多一条边将它们连上后得到一个无奇顶点的连通图。由上知这个图是一个环,因此去掉新加的边后成为一条路径,起点和终点是 u 和 v。证毕。

一笔画问题

时间限制: 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

思路:先判断连通, 即遍历一个顶点之后(将这个点所有能走到的点进行标记),如果还有没遍历到的点说明这个图不连通,再判断度数

即有0个或者2个奇度点就是欧拉路径


此题代码注意:

1   标记已走过点的顺序,是先标记再走下一个点还是先走下一个点再标记

2   初始化图很关键

3   用到了vector的  push_back,     clear 注意一下

代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

#include<vector>
#define maxlen 1005
vector<int> map[maxlen];
int discovered[maxlen];
int degree[maxlen];//保存每个节点的度
int c;
int p, q;


void dfs(int m);
int judge();
void read();

int main()
{
//	freopen("in.txt", "r", stdin);
	int n, count;//count:奇度顶点的个数
	scanf("%d", &n);
	while(n--){

		for(int i = 0; i < maxlen; i++)
			map[i].clear();
		memset(degree, 0, sizeof(degree));
		memset(discovered, 0, sizeof(discovered));
		c = count = 0;

		read();
	
		if(judge()){//遍历所有点统计奇度点个数
			for(int i = 1; i <= p; i++){
				if(degree[i] % 2)
					count++;
				}	
				if(count == 0 || count == 2)//一个连通图为欧拉路径当且仅当奇度顶点的数目为0 或 2
					printf("Yes\n");
				else
					printf("No\n");
			}
		else
			printf("No\n");
	
	}
	return 0;
}

void read(){//读图
	int a, b;
		scanf("%d%d", &p, &q);
			while(q--){
				scanf("%d%d", &a, &b);
				map[a].push_back(b);
				map[b].push_back(a);
				degree[a]++;
				degree[b]++;
			}
	
}

int judge(){//判断连通性
	dfs(1);
	for(int i = 1; i <= p; i++)
		if(discovered[i] == 0){
			c = 1;
			break;
		}
	if(c == 1)
		return 0;
	else
		return 1;
}

void dfs(int m){//把连通的点进行标记
	int i, next;	
	discovered[m] = 1;
	for(i = 0; i < degree[m]; i++ ){
		next = map[m][i];
		if(discovered[next] == 0){
			dfs(next); 
		}
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值