欧拉道路与欧拉回路
欧拉道路是指不重复的经过图的每一条边所形成的道路
欧拉回路是指不重复的经过图的每一条边所形成的回路
这类问题都可以使用dfs来求解
下面给出几道例题
1.P6066 [USACO05JAN] Watchcow S
解析:
一道模板题,建好双向边,走过一次删掉一条
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1e5+39+7; int n,m,vis[N]; struct node{ int to,visit; }; stack< int >st; vector<node>G[N]; void dfs( int x){ for ( int i=vis[x];i<G[x].size();i=vis[x]){ vis[x]=i+1; if (G[x][i].visit) continue ; G[x][i].visit=1; dfs(G[x][i].to); } st.push(x); } int main(){ cin>>n>>m; for ( int i=1;i<=m;i++){ int x,y;cin>>x>>y; G[x].push_back({y,0}); G[y].push_back({x,0}); } dfs(1); while (st.size()){ cout<<st.top()<< '\n' ; st.pop(); } return 0; } |
解析:
模板题,判断有向图是否存在欧拉路径,只需要看度,如果存在入度和出度不等的,判断一下,分三种情况:1.入度-出度=1,cnt++ 2.出度-入度=1,cnt1++,并将起点设为当前点的编号 3.直接输出No
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1e5+39+7; vector< int >G[N]; stack< int >st;pair< int , int >cnt={0,0}; int n,m,vis[N],din[N],dout[N],is=1,s=1; void dfs( int x){ for ( int i=vis[x];i<G[x].size();i=vis[x]){ vis[x]=i+1; dfs(G[x][i]); } st.push(x); } int main(){ cin>>n>>m; for ( int i=1;i<=m;i++){ int x,y;cin>>x>>y; G[x].push_back(y); din[y]++;dout[x]++; } for ( int i=1;i<=n;i++)sort(G[i].begin(),G[i].end()); for ( int i=1;i<=n;i++){ if (din[i]!=dout[i]){ is=0; if (din[i]-dout[i]==1)cnt.first++; else if (dout[i]-din[i]==1){ cnt.second++; s=i; } else { cout<< "No" ; return 0; } } } if ((!is)&&!(cnt.first==cnt.second&&cnt.first==1)){ cout<< "No" ; return 0; } dfs(s); while (st.size()){ cout<<st.top()<< ' ' ; st.pop(); } return 0; } |
解析:
使用字母编号跑欧拉路径的模版即可,编号规则:A~Z为1~26,a~z为27~52
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1e3+39+7; int calc( char c){ if (c<= 'z' &&c>= 'a' ) return c- 'a' +27; else return c- 'A' +1; } char uncalc( int n){ if (n>=27&&n<=52) return n+ 'a' -27; else return n+ 'A' -1; } int n,m,vis[N],d[N],a[N][N]; stack< int >st; void dfs( int x){ for ( int i=1;i<55;i++){ if (!a[x][i]) continue ; a[x][i]=a[i][x]=0; dfs(i); } st.push(x); } int main(){ cin>>n; int k=0x3f3f3f3f; for ( int i=1;i<=n;i++){ string s;cin>>s; a[calc(s[0])][calc(s[1])]=a[calc(s[1])][calc(s[0])]=1; ++d[calc(s[0])];++d[calc(s[1])]; k=min(k,min(calc(s[0]),calc(s[1]))); } int cnt=0,t=0x3f3f3f3f; for ( int i=1;i<=55&&cnt<=2;i++){ if (d[i]%2){ cnt++; t=min(t,i); } } if (cnt==1||cnt>2){ cout<< "No Solution" ; return 0; } if (cnt==0)dfs(k); else dfs(t); while (st.size()){ cout<<uncalc(st.top()); st.pop(); } return 0; } |
4.求子树的权值之和
解析:
使用前缀和,欧拉序等维护权值和,最后前缀和求和即可
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1e5+39+7; vector< int >G[N]; int e[N],pos[N][2],len,a[N],sub[N]; void dfs( int x, int fa){ e[++len]=x; pos[x][0]=len; int SIZE=G[x].size(); for ( int i=0;i<SIZE;i++){ if (G[x][i]==fa) continue ; dfs(G[x][i],x); } e[++len]=x; pos[x][1]=len; } int main(){ int n,m,t;cin>>n>>m>>t; for ( int i=1;i<=n;i++)cin>>a[i]; for ( int i=1;i<n;i++){ int a,b;cin>>a>>b; G[a].push_back(b); G[b].push_back(a); } dfs(1,0); sub[1]=a[e[1]]; for ( int i=2;i<=len;i++)sub[i]=a[e[i]]-a[e[i-1]]; for ( int i=1;i<=m;i++){ int x,w;cin>>x>>w; sub[pos[x][0]]+=w; sub[pos[x][1]+1]-=w; } for ( int i=1;i<=len;i++)sub[i]+=sub[i-1]; for ( int i=1;i<=len;i++)sub[i]+=sub[i-1]; while (t--){ int x;cin>>x; cout<<(sub[pos[x][1]]-sub[pos[x][0]-1])/2<< '\n' ; } return 0; } |
哈密顿路径与欧拉路径相似,它是一个遍历所有顶点的路径,还有哈密顿回路,顾名思义,就是一条遍历所有顶点的回路
下面给出一道例题
最短哈密顿回路
解析:
使用状压DP,记录所有可能性,进行DP求解最小值的过程
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1<<21,M = 25; int n,G[M][M],dp[N][M]; int main(){ memset (dp,0x3f, sizeof (dp)); cin>>n; for ( int i=0;i<n;i++){ for ( int j=0;j<n;j++){ cin>>G[i][j]; } } dp[1][0]=0; for ( int i=1;i<(1<<n);i++){ for ( int j=0;j<n;j++){ if ((i>>j)&1){ for ( int k=0;k<n;k++){ if ((i>>k)&1){ int l=i^(1<<j); dp[i][j]=min(dp[i][j],dp[l][k]+G[j][k]); } } } } } cout<<dp[(1<<n)-1][n-1]; return 0; } |
顺便再提一下dfs序
dfs序就是dfs遍历所形成的的序列,这里面需要引入一个新知识点,即时间戳,用来计算第一次和最后一次遍历当前点的时间,非常有用,可以判断y是否为x子节点
下面放上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include<bits/stdc++.h> #define ll long long using namespace std; const int N = 1e5+39+7; int n,m,s[N],dfn[N],tim=0,e[N],pos[N],len=0; vector<vector< int > >G[N]; void dfs( int x, int fa){ int u=len+1; s[++len]=++tim;dfn[len]=x; pos[x]=len; int SIZE=G[x].size(); for ( int i=0;i<SIZE;i++){ if (G[x][i]==fa) continue ; dfs(G[x][i],x); } e[u]=tim; } int main(){ cin>>n>>m; for ( int i=1,a,b;i<=m;i++){ cin>>a>>b; G[a].push_back(b); G[b].push_back(a); } dfs(1,0); for ( int i=1,a,b;i<=n;i++){ cin>>a>>b; a=pos[a];b=pos[b]; if (s[a]<=s[b]&&e[b]<=e[a])cout<< "Yes\n" ; else cout<< "No\n" ; } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现