洛谷 P1341 无序字母对
【题目链接】
洛谷 P1341 无序字母对
【题目考点】
1. 欧拉图:欧拉路径(无向图)
【解题思路】
每个字母是一个顶点,每个字母对是一条边。“无序即字母对中的两个字母可以位置颠倒”,意味着这样的边连接的两个字母没有先后顺序,是无向边,形成的图是无向图。
给定n个不同的字母对,也就是n个不同的边(没有重边),要得到一个有n+1个字母的字符串,每个字母对都出现。相邻两个字母是一个字母对,n+1个字母的字符串中可以出现n个字母对,也就是说给定的n个字母对都要出现。
该字符串相当于图中的一条路径,字符串中相邻的字母构成的字母对是一条边。要想让所有字母对都出现,也就是该路径要经过图中所有的边,即该路径是欧拉路径。
该问题抽象为:每个字母是一个顶点,每个字母对是一条边,求该无向图中的欧拉路径。
大写与小写字母的ASCII码在0~127范围内,可以直接用字母的ASCII码作为该字母对应顶点的编号。
判断无向图有欧拉路径的条件
- 所有顶点都是偶数度
- 只有两个顶点是奇数度,其它顶点是偶数度
输入边的同时,统计顶点的度。
遍历所有顶点,统计度为奇数的顶点数。
由于要求输出字典序最小的方案,因此必须选择可能作为起点的编号最小的顶点。
- 如果所有顶点都是偶数度,那么按编号从小到大遍历所有顶点,选择第一个度大于0的顶点作为起点
- 如果存在两个度为奇数的顶点,那么按编号从小到大遍历所有顶点,选择第一个奇数度顶点作为起点
- 对于其它情况,该图不存在欧拉路径,输出"No Solution"
从起点出发,调用Hierholzer算法,求出欧拉路径,最后把欧拉路径转化成字符串输出。
【题解代码】
写法1:使用邻接矩阵
#include <bits/stdc++.h>
using namespace std;
#define N 128
int m, n = 128, edge[N][N], deg[N];
stack<char> stk;
void dfs(int u)
{
for(int v = 0; v < n; ++v)
{
if(edge[u][v])
{
edge[u][v] = edge[v][u] = 0;
dfs(v);
}
}
stk.push(u);
}
int main()
{
int oddNum = 0;//奇数度顶点数量
char f, t, st;
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t;//scanf("%c%c\n", &f, &t);
edge[f][t] = edge[t][f] = 1;//无重边
deg[f]++, deg[t]++;
}
for(int v = 0; v < n; ++v)
if(deg[v] > 0 && deg[v]%2 == 1)
oddNum++;
if(oddNum == 0)//有欧拉回路
{
for(int v = 0; v < n; ++v)
if(deg[v] > 0)
{
st = v;
break;
}
}
else if(oddNum == 2)//有欧拉路径
{
for(int v = 0; v < n; ++v)
if(deg[v]%2 == 1)
{
st = v;
break;
}
}
else
{
cout << "No Solution";
return 0;
}
dfs(st);
while(stk.empty() == false)
{
cout << stk.top();
stk.pop();
}
return 0;
}
写法2:使用邻接表
#include<bits/stdc++.h>
using namespace std;
#define N 130
#define M 1330 //边最大数量,为52个结点中选两个连边的数量:C(52, 2) = 1326
struct Node
{
int v, e;//v:顶点编号 e:边编号
Node(){}
Node(int a, int b):v(a), e(b){}
bool operator < (Node b)
{
return v < b.v;
}
};
int n, deg[N], beg[N];//beg[i]:edge[i]从下标beg[i]开始,下标更前面的顶点相当于已被删掉
vector<Node> edge[N];
bool vis[M];
stack<char> stk;
void dfs(int u)
{
for(int &i = beg[u]; i < edge[u].size(); ++i)
{
int v = edge[u][i].v, e = edge[u][i].e;
if(vis[e] == false)
{
vis[e] = true;
dfs(v);
}
}
stk.push(u);
}
int main()
{
char f, t, st;
int oddNum = 0;//奇数度顶点数量
cin >> n;
for(int i = 1; i <= n; ++i)
{
cin >> f >> t;
edge[f].push_back(Node(t, i));
edge[t].push_back(Node(f, i));
deg[f]++, deg[t]++;
}
for(int i = 0; i < 128; ++i)
sort(edge[i].begin(), edge[i].end());//使每个vector有序,以输出字典序最小的序列。
for(int v = 0; v < 128; ++v)
if(deg[v]%2 == 1)
oddNum++;
if(oddNum == 0)
{
for(int v = 0; v < 128; ++v)
if(deg[v] > 0)
{
st = v;
break;
}
}
else if(oddNum == 2)
{
for(int v = 0; v < 128; ++v)
if(deg[v]%2 == 1)
{
st = v;
break;
}
}
else
{
cout << "No Solution";
return 0;
}
dfs(st);
while(stk.empty() == false)
{
cout << stk.top();
stk.pop();
}
return 0;
}