P1341 无序字母对 题解
Author: Yuyi
题目分析与推导过程
这道题可以说是之前做过的所有欧拉路径题目的“终极结合版”。
题目要求把一系列的无序字母对串联成一个完整的字符串,并且要求字典序最小。如果把每个字母看作“图中的顶点”,把每个字母对看作“连接这两个字母的无序边”,那么“串联成包含所有字母对的字符串”完美等价于:在无向图中寻找一条欧拉路径。
在这个背景下,本题融合了我们刚刚积累的所有经验:
- 无向图欧拉路径判定:我们同样只需统计每个点(字母)的度数。如果奇数度数的字母个数为 0,说明是欧拉回路;如果恰好为 2,说明是欧拉通路;如果是其他任何数量,则直接判定为
No Solution。
- 字典序最小的贪心策略:因为节点都是字符,我们可以直接利用它们本身的 ASCII 码特性。创建一个
G[150][150] 的二维邻接矩阵来存图。在寻找起点时:
- 如果是欧拉回路(0个奇数度点),从拥有连边的、ASCII 码最小的字符作为起点开始。
- 如果是欧拉通路(2个奇数度点),直接选择这两个奇数度点中 ASCII 码较小的那一个作为起点。
- 在 DFS 跑图时,因为我们的内层循环是
rep(v, 0, 127),这天生就保证了我们每一次都会优先探索字典序较小的字母,连排序都省了!
- 图连通性的隐藏大坑:这是本题与前几道题唯一、也是最致命的区别——题目没有保证图是连通的! 也就是说,有可能会存在两组毫不相干的字母对在那边“自娱自乐”。
那么如何最优雅地判定连通性呢?其实完全不需要额外去写冗长的并查集。我们只需要在 DFS 结束之后,看一眼压入答案栈中的节点数量
cnt 是否等于 n+1 即可。如果不等于,那就说明图断开了,有边根本没遍历到,直接输出 No Solution 即可。
正解代码
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define eps 1e-9
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define lowbit(a) (a)&(-a)
using namespace std;
const int maxn = 1e5+5;
const int mo = 998244353;
const double pi = acos(-1.0);
template <typename T>
inline void read(T &X)
{
X = 0;int w = 0; char ch = 0;
while(!isdigit(ch)) {w |= ch == '-' ;ch = getchar();}
while(isdigit(ch)) X = X * 10 + (ch^48),ch = getchar();
if(w) X = -X;
}
int n;
int G[150][150];
int deg[150];
char ans[maxn];
int cnt;
void dfs(int u)
{
rep(v,0,127)
{
if(G[u][v] > 0)
{
G[u][v]--;
G[v][u]--;
dfs(v);
}
}
ans[++cnt] = u;
}
void solve()
{
read(n);
int start_node = 150;
rep(i,1,n)
{
string s;
cin >> s;
int u = s[0];
int v = s[1];
G[u][v]++;
G[v][u]++;
deg[u]++;
deg[v]++;
start_node = min({start_node, u, v});
}
int odd_cnt = 0;
int S = 0;
rep(i,0,127)
{
if(deg[i] % 2 == 1)
{
odd_cnt++;
if(S == 0) S = i;
}
}
if(odd_cnt != 0 && odd_cnt != 2)
{
cout << "No Solution\n";
return;
}
if(S == 0) S = start_node;
dfs(S);
if(cnt != n + 1)
{
cout << "No Solution\n";
return;
}
dep(i,cnt,1) cout << ans[i];
cout << "\n";
return;
}
int main()
{
int T_case=1;
while(T_case--) solve();
return 0;
}
Generated by Gemini 3.1 Pro