题目链接:https://www.luogu.org/problem/P1341
题意:给你n个无序对(位置可以颠倒),但必须相邻,问能否构造一个长为n+1的字符串,使得包含每个无序对,如果可以,输出字典序最小的字符串
分析:必须包含每个无序对,每个无序对必须相邻,我们可以抽象为图论问题,无序对间连一个无向边,那么问题就转化为一笔画问题了
即能不能在图中找到一个欧拉回路或者欧拉通路
注:欧拉通路:经过图上的每一条边
一开始要先用并查集判断是否联通
无向图判断欧拉回路:度数全为偶数
无向图判断欧拉通路:除了两点为奇数,其余度数全为偶数,存在以该两点为起始点的欧拉通路
有向图判断欧拉通路:入度等于出度
有向图判断欧拉回路:最多有一点入度等于出度+1,最多有一点入度等于出度-1,就会有一条从出度大于入度(没有则等于)的点出发,到达出度小于入度(没有则等于)的点的一条欧拉路径。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const ll mod=1000000007; const int maxn=1e5+7; int mp[200][200]; char in[2]; int du[200],fa[200];//分别用来存储度数和父亲 int n; char ans[3000]; int find(int x){ if(x!=fa[x]) return fa[x]=find(fa[x]); return x; } void dfs(int x){ for(int i=65;i<=122;i++){ if(mp[x][i]){ mp[x][i]=0; mp[i][x]=0; dfs(i); } } ans[n--]=x;//因为是回溯的时候加值,故倒着存 } int main(){ //A的编码为65,z的为122 for(int i=65;i<=122;i++)fa[i]=i;//并查集基操 scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",in); mp[in[0]][in[1]]=1; mp[in[1]][in[0]]=1; du[in[0]]++,du[in[1]]++; int x=find(in[0]),y=find(in[1]); fa[x]=y; } int num=0; for(int i=65;i<=122;i++) if(i==fa[i]&&du[i]) num++;//判断是否只有一个连通分量即连通 if(num!=1){ printf("No Solution\n"); return 0; } num=0; int s=0;//标记起点,如果存在欧拉回路就是字典序最小的,如果存在欧拉通路就是两点先出现的那个(字典序更小) for(int i=65;i<=122;i++){ if(du[i]%2){//度数为奇数的点个数记录下来 num++; if(s==0) s=i; } } if(num&&num!=2){//既无欧拉通路也无欧拉回路 printf("No Solution\n"); return 0; } if(num==0){//有欧拉回路 for(int i=65;i<=122;i++){ if(du[i]){ s=i; break; } } } dfs(s); printf("%s\n",ans); return 0; }