洛古--P1341 无序字母对--欧拉路径--Sabrina--Sabrinadol

题目网站
题目描述
给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。

输入输出格式
输入格式:
第一行输入一个正整数n。

以下n行每行两个字母,表示这两个字母需要相邻。

输出格式:
输出满足要求的字符串。

如果没有满足要求的字符串,请输出“No Solution”。

如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案

输入输出样例
输入样例#1:
4
aZ
tZ
Xt
aX
输出样例#1:
XaZtX

说明
【数据规模与约定】

不同的无序字母对个数有限,n的规模可以通过计算得到。

1.看到本题的第一印象是很蒙的,因为这种题也不好模拟啊,深搜代码一开始也是十分不好打的,于是就开始去建立图

2.当想到去建立图之后,问题就很是明朗了,题目的意思难道不就是从一个字母出发,输入数据中每两个字母有一条路,在我们的结果中必须要将这条路遍历到,并且该图为无向图,也就是我们只需要找一条欧拉路径走完每一条边

3.那么何为欧拉路径(这里简单叙述一下)

欧拉路径就是:在一个图G中找到一条路劲经过每条边并且仅经过一次的路径
再来看本题,是否就是需要在一个无向图中找到一条欧拉路径,并且将路径输出

那如何判断是否存在欧拉路径:

在无向图中,下面两种情况都可以找到一条合法的欧拉路径
(度就是该点所连的边,被指向的为入度,反之为出度)
欧拉图(存在欧拉回路):该图是连通的且每个点的度数都是偶数

半欧拉图(存在欧拉路径):该图连通且仅有两个点的读数是奇数,这两个点即为欧拉路径的起始点

4.代码讲解

(1)解决输入及预处理问题

1. 这里直接使用二维数组进行存图,因为是无向图所以在存的时候要双向;
 2.由3可知我们如果要判断是否无解,直接就判断该图是否为欧拉图或半欧拉图就好了,所以在输入的时候需要存度(无向图我们就不管入度 出度),然后遍历进行判定,如果有点的度不为偶数,且这样的点的个数不等于2,那么我们直接输出无解
(如果是二,就是一头一尾的半欧拉图)
3.同时又因为,在最后输出的时候要使字典序更小,所以我们在找开始点的时候,就从小开始找,符合条件的入手点,这里有个小问题。就是在我们找这个点的时候有可能会发生错误,下面为策略。首先,我们不知道该图是否是半欧拉图,所以一开始我们找到有度为基数的点,就将start赋值为该点。循环结束后,我们再判定start是否为零,如果为零,说明没有找到基数的点,再找到队列中字典序最小的点作为start就好。如果start不为0,就判定一共有几个基数点,如果不等于2直接return 0;,如果等于2,就以此作为起始点
4.这一段的代码如下
	cin>>n;
	char b,c;
	for(int i=1;i<=n;i++)
	{
		cin>>b>>c;
		a[b][c]++;
		a[c][b]++;
		du[c]++;
		du[b]++;
	}
	int ans=0;
	for(int i=0;i<=150;i++)
	{
		if(du[i]%2==1)
		{
			ans++;
			if(!start)	start=i;	
		}
	}	
	if(!start)
	{
		for(int i=0;i<=150;i++)
		{
			if(du[i]) 
			{
				start=i;
				break;
			}
		}
	}
	if(ans!=0&&ans!=2)
	{
		cout<<"No Solution"<<endl;
		return 0;
	}

(2)深搜求解
接下的步骤看似复杂,其实就是一个基础的深搜
从起点开始,从小到大一直找,找到有路就向下递归,再回溯时候赋值

void find (int k)
{
	for(int i=0;i<=150;i++)//省事
	{
		if(a[k][i])//如果有路就搜
		{
			a[k][i]--;//遍历过的路就毁掉,保证只走一次
			a[i][k]--;
			dfs(i);
		}
	}
	sum++;
	op[sum]=k;
	//这里需要注意的是,因为我们是深搜完成之后再进行的赋值,所以越后扫到的数越先入队,所以输出时倒序
	return ;
}

(3)附上AC代码,上面片段代码已经很详细了,所以这段代码就没有过多注释了

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 10005
using namespace std; 
int n;
int  start;
int du[maxn];
int a[maxn][maxn];
int flag=0;
char op[10050];
int sum=n+1;
void find (int now)
{
	for(int i=0;i<=150;i++)
	{
		if(a[now][i])
		{
			a[now][i]--;
			a[i][now]--;
			find(i);
		}
	}
	sum++;
	op[sum]=now;
	return ;
}
int main()
{
	cin>>n;
	char b,c;
	for(int i=1;i<=n;i++)
	{
		cin>>b>>c;
		a[b][c]++;
		a[c][b]++;
		du[c]++;
		du[b]++;
	}
	int ans=0;
	for(int i=0;i<=150;i++)
	{
		if(du[i]%2==1)
		{
			ans++;
			if(!start)	start=i;	
		}
	}	
	if(!start)
	{
		for(int i=0;i<=150;i++)
		{
			if(du[i]) 
			{
				start=i;
				break;
			}
		}
	}
	if(ans!=0&&ans!=2)
	{
		cout<<"No Solution"<<endl;
		return 0;
	}

	find(start);
	if(sum<n+1)
	{
		cout<<"No Solution"<<endl;
		return 0;
	}
	for(int i=sum;i>=1;i--)
	cout<<op[i];
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值