【原创】一笔画问题(欧拉路)

一笔画问题(euler-circuit.cpp)


题目描述

 

 

对给定的一个无向图,判断能否一笔画出。若能,输出一笔画的先后顺序,否则输出“No Solution!”
所谓一笔画出,即每条边仅走一次,每个顶点可以多次经过。
输出字典序最小的一笔画顺序。

 

 

输入

 

 

第1行:1个整数n,表示图的顶点数(n<=100)
接下来n行,每行n个数,表示图的邻接矩阵

 

 

输出

 

 

第1行:一笔画的先后顺序,每个顶点之间用一个空格分开

 

 

样例输入

样例一

 

3
0 1 1 
1 0 1 
1 1 0 

 

样例二

 

 

7
0 1 0 1 1 0 1 
1 0 1 0 0 0 0 
0 1 0 1 0 0 0 
1 0 1 0 0 0 0 
1 0 0 0 0 1 0 
0 0 0 0 1 0 1 
1 0 0 0 0 1 0 

 

 

 

样例输出

样例一1 2 3 1

 

样例二

 

1 2 3 4 1 5 6 7 1

 

做这道题之前,我们先来了解一下一笔画。

欧拉定理:①当图是连通的,且有且仅有2个点的度为奇数,那么从其中一个点出发,另外一点结束。这是欧拉路。

②当图是连通的,且0个点的度数为奇数,那么从任意一个点出发,这个点结束。这是欧拉回路。

③当图不是连通的,或者奇点的个数不为0、2,就一定无解。

 

那么,怎样知道从哪个点出发,到哪个点为止呢?很简单,输入时,判断该点的度是否为奇数,如果奇数个数为0,从1号点出发;如果为2,从第一个奇点出发;否则,无解。

有人可能会问,为什么从1出发呢疑问?因为结果要求我们输出“字典序最小的一组解”,当0个奇点时,从任意一个点出发都可以找到解,从1出发就可以确保解字典序最小啦!~\(≧▽≦)/~

有人可能又会问,怎么存储解呢疑问?又怎么存储解的个数呢疑问?可以在函数里存储,即代码里的ans[len++]=i;

注意!(∩_∩)这个代码相当于:ans[len]=1;len++; 与ans[++len]=i;是不同的哟!

再注意!由于代码里存储在函数底部,所以数组储存是这么存的:找到解后,依次保存值然后退出,所以这样ans数组就是倒着储存的,所以输出的时候也要倒着输出哟!~\(≧▽≦)/~

有人可能还会问,你是好奇宝宝吗疑问这么多问题疑问?不回答了抓狂

详见代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<functional>
using namespace std;
int p,q,r,n,a[123][123],len,ans[100000],begin=1;
void search(int i)
{
	for(int j=1;j<=n;j++)
	{
		if(a[i][j])
		{
			a[i][j]=a[j][i]=0;
			search(j);
		}
	}
	ans[len++]=i;
}
void in()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int sp=0;
		for(int j=1;j<=n;j++)
		{
			cin>>p;
			if(p)
			{
				sp++;
				a[i][j]=a[j][i]=1;
			}
		}
		if(sp%2==1)
		{
			if(r==0) begin=i;
			r++;
		}
	}
}
void out()
{
	for(int i=len-1;i>=0;i--)
	{
		if(i!=len-1) printf(" ");
		printf("%d",ans[i]);
	}
}
int main()
{
	freopen("euler-circuit.in","r",stdin);
	freopen("euler-circuit.out","w",stdout);
	in();
	if(r==2 || r==0) 
	{
		search(begin);
		out();
	}
	else printf("No Solution!");
}

 

UPD 2019.04.08

必须倒着存点。

 

 

 

如果正着存储就会出现如上述的荒唐错误。

如果正着存的话,在1->2->3->4->1->5->6 之后,它会优先走编号较小的7号点(走了7再走1之后没有路了返回6号),然后再走编号较大的8号点(然后走9,走10,走回6号)。

而我们都知道正确方法是先把6,8,9,10这个正方形走完了以后再走7回去。

正着存存下来的是我们访问节点的顺序,是把所有边走一遍的字典序最小的遍历顺序。

而这个字典序最小的遍历顺序不总是一笔画的字典序最小的遍历顺序。

那么为什么倒着存就对了呢?

倒着存,也就是先递归后续的点完再存自己,也就是先把那些支出来的图形(比如说那个6,8,9,10的正方形)给走完,保证了我们存下来的顺序是一笔画的顺序。

所以正确答案是:1 2 3 4 1 5 6 8 9 10 6 7 1

 

 

 

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值