欧拉路径学习笔记

news/2024/10/20 16:40:31

简介

定义:

  • 欧拉回路:通过图中每条边恰好一次的回路
  • 欧拉通路:通过图中每条边恰好一次的通路
  • 欧拉图:具有欧拉回路的图
  • 半欧拉图:具有欧拉通路但不具有欧拉回路的图

摘自: oi-wiki。

定义说白了就是小学的一笔画问题,这里直接给出三道例题。P7771 【模板】欧拉路径,CF508D 和 CF36E。

例题

P7771 【模板】欧拉路径

思路

模板题,没有思路。直接讲一下求欧拉路径的方法即可。

首先,对于一个图,它要存在欧拉路径的必要条件很简单。就是奇点的个数要为 0 或者 2。其他情况都是无解的。这两种情况对应的图的形态长这样捏:

当然这也不是唯一解。其中第一个图中没有奇点,而第二个图中奇点为 1 和 2。

这里给出求解欧拉路径的伪代码:

void dfs(int u){遍历u的所有出边如果该边还存在删掉这条边dfs(该边去往的点)u入栈
}

这里给出一点 luogu 的大部分题解都没有解释到的地方。为什么不能遍历到一个点就将该点入栈,非要循环结束后才可以?这里举出一个例子:如果有一个无向图长得像一个 8 字,就像下面这样:

当我们走到 5 的时候会发生一个事情。就是说这个时候我们既可以选择往 1 走也可以往 4 走。我们希望的是向 4 走。如果我们最后再入栈那么当它 dfs 到 1 的时候就只会推掉 5 和 1 之间的部分,再从 5 开始继续搜索。相当于我们最开始的 \([1,7,6,5]\) 加上 \([5,4,3,5]\) 以及 \([5,1]\) 就是我们最后的答案。这里需要倒序输出,这里借鉴了 Marsrayd 大佬的 博客。这里是这样说的:

感性理解倒序输出的原因:如果是欧拉回路,那么遍历到哪,输出到哪也是对的,因为不管怎么走都会绕某个环走回起点,所以不到最后不会出栈,然而欧拉路径会出现边都被走过了,走不回起点,最后会停留在终点,遇到这种情况这种路径会最先出栈,于是只要把这个路径先走了,前面就和欧拉回路一样随便走就行,不会出栈,于是倒序输出就是对的

膜拜大佬。这个说得非常清楚啊。

CF508D

思路

此题很板啊。我们直接把这个字符串的前面的两个字符和后面的两个字符各看作一个点,连一条单向边。因为这题要求相同的字符串之间连线。那么我们把它转成 int 了之后就自然而然连上边了。

我们需要再记录一个每个点的出度和入度。来判断改图是否存在欧拉路径。

其实就是一个板题,这边给出可爱的代码:

AC code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace WYL{const int N=3e5+10;const int INF=2e5;vector<int> edge[N];vector<int> point;int n,outde[N],inde[N],Start;string ans;void dfs(int u){while(!edge[u].empty()){int to=edge[u][edge[u].size()-1];edge[u].pop_back();dfs(to);}ans+=(char)(u%256);}int main(){cin>>n;memset(outde,0,sizeof(outde));memset(inde,0,sizeof(inde));string s;for(int i=1;i<=n;i++){cin>>s;int u=s[0]*256+s[1],v=s[1]*256+s[2];edge[u].push_back(v);outde[u]++;inde[v]++;Start=u;}int k=3e5;for(int i=0;i<=k;i++){if(edge[i].size()!=0){point.push_back(i);}}if(n==1){cout<<"YES"<<endl;cout<<s<<endl;return 0;}int flag=0,cnt=0;for(int i=0;i<=INF;i++){if(outde[i]!=inde[i]){flag=1;}if(abs(outde[i]-inde[i])==1){cnt++;}if(abs(outde[i]-inde[i])>1&&outde[i]!=inde[i]){cout<<"NO"<<endl;return 0;}if(outde[i]-inde[i]==1){Start=i;}}if(flag==1&&cnt!=2){cout<<"NO"<<endl;return 0;}dfs(Start);ans+=Start/256;if(ans.size()<n+2){cout<<"NO"<<endl;return 0;}cout<<"YES"<<endl;reverse(ans.begin(),ans.end());cout<<ans<<endl;return 0;}
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);WYL::main();return 0;
}

CF36E

写在前面的话

也许是本人太蒟了。本人认为本题的代码难度远高于 CF508D,但是 CF 的评分只差了 100 分(喜。

题意

翻译说得很清楚,这里再重复一下。

给你一张无向图,不保证连通以及可能有重边。让你求两条路径,使其正好能够覆盖所有的边,按边的编号输出答案。

思路

首先。这道题与 CF508D 的不同点在于这道题要求的是两条路径。于是我们尝试分类讨论。

因为不保证连通。我们首先知道,如果连通块的数量是 1 或者 2 的时候都有解。为什么?这个很显然,如果有大于两个的连通块。那么一定不存在两条路径能够覆盖掉所有的点。于是连通块数量大于 2 时我们可以直接把这个图 ban 掉。

好的现在我们来看连通块为 1 的情况。因为我们要求的路径相当于就是一个欧拉路径。所以在连通块为 1 的情况下度数为奇数的个数就只可能是 0,2 或者 4。其中呢 0 和 2 都比较好处理。只要当路径长度比 2 大就一定可以分成两坨。但是呢 4 就需要想一想了。这里提供一种蒟蒻的想法:我们找到两个没有连边的奇点将它们之间连上一条虚边(不是重链剖分里的那个捏)。跑一遍欧拉路径,然后再把那条虚边给删掉。得到的两个部分就是我们所求的,这里给出一个例子:

输入数据:

5
1 2 
1 4 
1 3
1 5
3 6

这里我连上了 5 与 6 之间的边。再删掉再删掉红色的边,就得到了点集为 \([1,4,5]\)\([1,2,3,6]\) 的答案。

连通块为 1 的情况看完了。现在来研究连通块数量为 2 的。显然只有在两个连通块的奇点个数都为 0 或者都为 2 时才是有解的。如果有解直接各找一个欧拉路径就可以啦。

AC code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace WYL{const int N=1e5+10;int n,to[N],nxt[N],head[N],tot,du[N],kid[N],kcnt,kminn[N];bool mark[N],vis[N];vector<int> jidian,path,jidian1,jidian2,path1,path2;void add(int u,int v){nxt[++tot]=head[u];head[u]=tot;to[tot]=v;return;}void dfs(int id,int k){kid[id]=k;for(int i=head[id];i;i=nxt[i]){if(kid[to[i]]==0&&du[to[i]]!=0){dfs(to[i],k);}}return;}int find_edge(int x,int y){for(int i=head[x];i;i=nxt[i]){if(to[i]==y&&!mark[(i+1)/2]){return i;}}return -1;}void Euler(int u,vector<int> &tmp){if(du[u]==0){tmp.push_back(u); return;}for(int i=head[u];i;i=nxt[i]){// cout<<u<<"->"<<to[i]<<endl;if(vis[(i+1)/2]){continue;}vis[(i+1)/2]=true;du[to[i]]--;du[u]--;// cout<<"***"<<u<<"->"<<to[i]<<endl;Euler(to[i],tmp);}tmp.push_back(u);return;}void solve(int l,int r,vector<int> path){for(int i=l;i<=r;i++){int opt=(find_edge(path[i],path[i+1])+1)/2;cout<<opt<<" ";mark[opt]=true;}return;}int main(){cin>>n;if(n==1){cout<<"-1"<<endl;return 0;}for(int i=1;i<=n;i++){int x,y;cin>>x>>y;du[x]++;du[y]++;add(x,y);add(y,x);}for(int i=1;i<=10005;i++){if(du[i]&&kid[i]==0){kminn[kcnt+1]=i;dfs(i,++kcnt);}if(du[i]%2==1){jidian.push_back(i);}}if(kcnt>2){cout<<"-1"<<endl;return 0;}if(kcnt==1){if(jidian.size()==4){for(int i=0;i<4;i++){for(int j=i+1;j<4;j++){if(find_edge(jidian[i],jidian[j])!=-1){continue;}swap(jidian[0],jidian[i]);swap(jidian[1],jidian[j]);goto end;}}end:int x=jidian[0],y=jidian[1];du[x]++;du[y]++;add(x,y);add(y,x);memset(vis,0,sizeof(vis));Euler(jidian[2],path);int place=0;while((path[place]!=jidian[0]||path[place+1]!=jidian[1])&&(path[place]!=jidian[1]||path[place+1]!=jidian[0])){place++;}cout<<place<<endl;solve(0,place-1,path);cout<<endl;cout<<n-place<<endl;solve(place+1,path.size()-2,path);return 0;}else{path.clear();if(jidian.size()!=0&&jidian.size()!=2){cout<<"-1"<<endl;return 0;}if(jidian.size()==0){Euler(kminn[1],path);}else if(jidian.size()==2){Euler(jidian[0],path);}cout<<"1"<<endl;solve(0,0,path);cout<<endl;cout<<path.size()-2<<endl;solve(1,path.size()-2,path);return 0;}}else if(kcnt==2){for(int i=0;i<jidian.size();i++){if(kid[jidian[i]]==1){jidian1.push_back(jidian[i]);}else{jidian2.push_back(jidian[i]);}}if(jidian1.size()!=0&&jidian1.size()!=2){cout<<"-1"<<endl;return 0;}if(jidian2.size()!=0&&jidian2.size()!=2){cout<<"-1"<<endl;return 0;}path1.clear();path2.clear();if(jidian1.size()==0){int place=1;while(kid[place]!=1){place++;}Euler(place,path1);}else{Euler(jidian1[0],path1);}cout<<path1.size()-1<<endl;solve(0,path1.size()-2,path1);cout<<endl;if(jidian2.size()==0){int place=1;while(kid[place]!=2){place++;}Euler(place,path2);}else{Euler(jidian2[0],path2);}cout<<path2.size()-1<<endl;solve(0,path2.size()-2,path2);return 0;}return 0;}
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);WYL::main();return 0;
}
/*107 47 46 12 32 11 64 28 44 83 5
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/73860.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

网站连接数据库怎么办

连接网站到数据库通常涉及以下几个步骤:选择数据库类型:常见的数据库类型有 MySQL、PostgreSQL、SQLite、MongoDB 等。 根据项目需求选择合适的数据库。安装数据库驱动:根据所选的数据库类型和开发环境,安装相应的数据库驱动。 例如,对于 Python 和 MySQL,可以使用 mysql…

td导航zlibrary镜像入口及国内可访问地址

TD导航是一个综合性的网址导航网站,它致力于为用户提供便捷、高效的上网体验。在这个平台上,用户可以轻松找到各类热门网站和实用工具,无论是新闻资讯、社交娱乐、购物消费,还是学习教育、工作办公等领域,TD导航都提供了丰富的资源链接。 TD导航的界面设计简洁明了,分类清…

20222422 2024-2025-1 《网络与系统攻防技术》实验二实验报告

一.实验内容 (1)使用netcat获取主机操作Shell,cron启动某项任务(任务自定) PS:cron是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程 (2)使用socat获取主机操作Shell, 任务计划启动 (3)使用MSF meterpreter(或其他软件)生成可执行文件,利用ncat或soc…

2024-2025-1(20241321)《计算机基础与程序设计》第四周学习总结

这个作业属于哪个课程 <班级的链接>(2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 <作业要求的链接>(2024-2025-1计算机基础与程序设计第一周作业)这个作业的目标 <了解并学习AI功能,回顾一周课程心得>作业正文 ... 本博客链接https://www.cnblo…

『模拟赛』多校A层冲刺NOIP2024模拟赛09(更新 T4)

『模拟赛记录』多校A层冲刺NOIP2024模拟赛09Rank 还行A. 排列最小生成树 (pmst) 签,有点可惜。 考虑连 \(i\) 与 \(i+1\) 时,所有边边权都是小于 \(n\) 的,因此我们只考虑边权小于 \(n\) 的边即可。因为边权为 \(|p_i-p_j|\times|i-j|\),所以只考虑 \(|p_i-p_j|\lt \sqrt{n…

云原生架构视图

关于云原生的概念,业内有没有统一的定义,比较主流的还是CNCF(Cloud Native Computing Foundation,云原生计算基金会)对云原生的定义。原文如下: Cloud native technologies empower organizations to build and run scalable applications in public, private, and hybrid …

2024-2025-1 20241328 《计算机基础与程序设计》第四周学习总结

学期(如2024-2025-1) 学号20241428《计算机基础与程序设计》第4周学习总结 作业信息这个作业属于哪个课程 <班级的链接>(如2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 <作业要求的链接>(如2024-2025-1计算机基础与程序设计第一周作业)这个作业的目标…