🍄前言
大家好,我是一勺黑猫。今天是每日一题的第二十八天,欢迎更多小伙伴加入到我们的打卡计划中,希望和你们在学习算法的路上一起进步~
🙎作者简介:一个正在努力学算法和后端的大三girl
?每日一题打卡地:高校算法学习社区
🎈联系方式:157543570(qq)
💨往期内容:
黑猫的每日一题https://blog.csdn.net/xingziyy/category_11731303.html
🍄今日题目
P1341 无序字母对 - 洛谷 | 计算机科学教育新生态
🌰思路:这道题用到了欧拉路的思想,我们先来简单了解一下欧拉路:
1. 定义
欧拉路径:如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。 欧拉回路:如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
2. 无向图的充要条件
欧拉路径:奇数点的数量是0(即欧拉回路)或2 欧拉回路:全是偶数点
3. 有向图的充要条件
欧拉路径:起点出度等于入度+1, 终点入度等于出度+1 欧拉回路:所有点的入度和出度相等
?由于本题是无序的字母对,也就相当于无向图。我们的任务就是找到一条欧拉路,使它包括每个无序字母对。根据欧拉路的定义,我们可以发现:如果图是不连通的,或者奇数度数的顶点存在但个数不为2时,在图中不存在欧拉路径,因此我们可以先把这种情况排除。判断图是否连通用并查集就好。
接下来,我们如何找到这条欧拉路呢?由于题目要求输出字典序最小的方案,所以对于欧拉回路(所有顶点的度数都是偶数)来说,我们每次都从字典序最小的顶点遍历就好;对于欧拉路径(此处指有两个顶点的度数是奇数的情况)来说,我们要从字典序最小的奇数度数顶点开始遍历,此后每次再遍历字典序最小的相邻顶点。需要注意的是,由于是递归遍历,所以存储路径时应该倒着存。
🌰AC代码:
#include<iostream>
#include<vector>
using namespace std;
int g[125][125];
int deg[125]; //存储每个顶点的度数
int father[125]; //存储父亲(祖先)节点
int n;
vector<char> v; //存储结果(欧拉路径)
//寻找祖先节点,采用压缩路径算法
int find(int x){
return father[x]=father[x]==x?x:find(father[x]);
}
//合并两个顶点
void u(int x,int y){
int xF=find(x),yF=find(y);
father[xF]=yF;
}
//欧拉,每次寻找字典序最小的相连顶点
void oula(int index){
for(int i='A';i<='z';i++){
if(g[index][i]){
g[index][i]=g[i][index]=0;
oula(i);
}
}
v[n--]=index; //由于是递归遍历,所以要倒着存
}
int main(){
cin>>n;
v.resize(n+1);
//初始化父亲数组
for(int i='A';i<='z';i++)
father[i]=i;
string s;
for(int i=0;i<n;i++){
cin>>s;
g[s[0]][s[1]]=g[s[1]][s[0]]=1;
deg[s[0]]++;
deg[s[1]]++;
u(s[0],s[1]);
}
int cnt=0; //记录祖先个数,也就是连通分量的个数
int ji_cnt=0; //记录度数为奇数的顶点个数
for(int i='A';i<='z';i++)
if(deg[i]){
if(father[i]==i)
cnt++;
if(deg[i]&1){
ji_cnt++;
}
}
//不是连通图或者不存在欧拉路径(回路)
if(cnt>1||ji_cnt&&ji_cnt!=2){
printf("No Solution");
return 0;
}
//寻找遍历的起点
int head=0;
for(int i='A';i<='z';i++)
if(deg[i]){
//如果是欧拉回路(所有顶点的度数都是偶数),就找字典序最小的顶点
if(!ji_cnt){
head=i;
break;
}
//如果是欧拉路径,就找字典序最小的度数为奇数的顶点
else if(deg[i]&1){
head=i;
break;
}
}
oula(head);
for(int i=0;i<v.size();i++)
cout<<v[i];
return 0;
}
如果让你有一丝收获,球球给一个三连支持!!!感谢大家??
?
|