题目描述
给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。
输入输出格式
输入格式:
第一行输入一个正整数n。
以下n行每行两个字母,表示这两个字母需要相邻。
输出格式:
输出满足要求的字符串。
如果没有满足要求的字符串,请输出“No Solution”。
如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案
输入输出样例
输入样例#1:
4
aZ
tZ
Xt
aX
输出样例#1:
XaZtX
说明
【数据规模与约定】
不同的无序字母对个数有限,n的规模可以通过计算得到。
先简单说一下欧拉这个问题,欧拉通路即一笔画问题,就是在不走重复边的情况下走完全部的边,如果能回到起点即是欧拉回路
首先,看到题目,再看到样例,就会发现字母对是[b ]必须[/b ]连在一起的,然后就会想到图的遍历之类的。有点灵感的话想到欧拉图应该没什么问题。
然后建图就很清晰了。将字母作为图的顶点,如果两字母间存在字母对就在相应的字母所对应的顶点连上一条五向边。题目要求的是字典序最小的欧拉通路,怎么办呢?
欧拉通路存在的要求是只存在0个或2个度数为奇数的顶点,如果没有度数为奇数的顶点,那么这个图还存在欧拉回路。如果有同学不知道什么是欧拉通路或欧拉回路请自己去百度。欧拉通路的求法很简单,直接暴力DFS回溯就好,在这道题中先走字典序最小的顶点就OK了,令人惊讶的是这样做的效率竟然真的很不错。
如果有同学想用递推来写的话,可能会因为提前走进死路而WA,加点判断的话,就我个人来说得了80,望神犇指导。
此外,欧拉回路还有一个更优美的方法叫套圈法(好像是这名字吧),有兴趣的可以百度一下。
一点都不优美的代码:
#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100;
int map[maxn][maxn], cnt[maxn], vis[maxn][maxn];
int n, maxm = 53;
stack <int> ans;
int zhuanhua1(char c)
{
if (c >= 'A' && c <= 'Z')
return (int)c - 'A';
return (int)c - 'a' + 26;
}
char zhuanhuan2(int x)
{
if (x >= 0 && x <= 25)
return (char)x + 'A';
return (char)(x - 26) + 'a';
}
void euler(int u)
{
for (int v = 0; v < maxm; v++)
if (map[u][v] && !vis[u][v])
{
vis[u][v] = vis[v][u] = 1;
euler(v);
ans.push(v);
}
}
int main()
{
scanf("%d%d", &n);
for (int i = 1; i <= n; i++)
{
char a, b;
cin >> a >> b;
int x = zhuanhua1(a), y = zhuanhua1(b);
map[x][y] = map[y][x] = 1;
cnt[x]++;
cnt[y]++;
}
int tot = 0, min1 = 1e8, min2 = 1e8;
for (int i = 0; i < maxm; i++)
{
if (cnt[i]%2==1)
{
tot++;
min1 = min(min1, i);
}
else if (cnt[i])
min2 = min(min2, i);
}
if (tot != 0 && tot != 2)
printf("No Solution\n");
else
{
int u;
if (tot == 0)
u = min2;
else
u = min1;
euler(u);
ans.push(u);
while (!ans.empty())
{
int temp = ans.top();
ans.pop();
char s = zhuanhuan2(temp);
cout << s;
}
printf("\n");
}
return 0;
}