整理的算法模板合集: ACM模板


目录

  • 非递归版
  • 普通递归版
  • HierholzersHierholzersHierholzers算法(输出字典序最小的答案)
  • FleuryFleuryFleury算法
  • 究极优化版
  • 混合图欧拉回路

欧拉路就是给一个图,存在一条回路从起点到终点把所边经过且每条边只经过一次。“一笔画问题

欧拉回路就是从起点遍历所有的边一次最后回到起点

非递归版

解决问题:给定一张无向图,求出它的欧拉回路,若不止一条,随意输出一条即可

int head[N],nex[M],ver[M],tot = 1;
bool vis[M];
int stk[M],ans[M];//模拟系统栈、答案栈
int n,m,s,t,cnt,top;
queue<int>q;void add(int x,int y){ver[++tot] = y;nex[tot] = head[x];head[x] = tot;
}void euler(){//模拟dfs递归stk[++top] = 1;//起点while(top > 0){int x = stk[top],i = head[x];while(i && vis[i])i = nex[i];//找到一条尚未访问过的路// 与x相连的所有边均已访问,模拟回溯过程,并记录if(i){stk[++top] = ver[i];vis[i] = ver[i ^ 1] = true;//正常的欧拉回路模板head[x] = nex[i];}else {// 与x相连的所有边均已访问,模拟回溯过程,并记录top--;ans[++cnt] = x;}}
}int main(){scanf("%d%d",&n,&m);tot = 1;over(i,1,m){int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);}euler();over(i,1,cnt)printf("%d\n",ans[i]);
}

普通递归版

HierholzersHierholzersHierholzers算法(输出字典序最小的答案)

此法输出的是所有合法方案中字典序最小的答案
本算法求出的是倒序的欧拉回路,所以输出的时候要倒序输出才是字典序最小的答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>using namespace std;const int N = 5000, M = 50007, INF = 0x3f3f3f3f;
typedef pair<int,int> PII;int n, m;
int d[N];
int g[N][N];
int ans[M], cnt;void dfs(int x){for(int i = 1;i <= 500;++i){if(g[x][i]){g[x][i] -- , g[i][x] -- ;dfs(i);}}ans[ ++ cnt] = x;
}int main(){scanf("%d", &n);for(int i = 1;i <= n;++i){int x,y;scanf("%d%d",&x, &y);g[x][y] ++ , g[y][x] ++ ;d[y] ++, d[x] ++;}int start = 1;while(!d[start])start ++;for(int i = 1;i <= 500;++i){if(d[i] % 2){start = i;break;}}dfs(start);for(int i = cnt;i; -- i)printf("%d\n",ans[i]);return 0;
}

输出n+1个字母,使得n个字母对都在这个字符串中出现,因为是n+1个字母,所以我们可以看出来其实就是一个欧拉路径,因为字母对可以替换顺序,所以我们将每个字母对都连一个无向边,建图,求欧拉回路,这样求出来的就是n+1个字母的字符串,因为是欧拉路径,所以会经过每条边,也就是每一个字母对都会在里面出现。

我们要先判断图是否连通(可以直接用并查集判断,或者看欧拉路径是否包含了n+1个点),如果不连通那么我们求出来的欧拉回路也不会包含所有的字母对。然后再判断是否有欧拉路径,判断的方法就是看奇度点的个数是不是0或者2,注意如果是0的话说明有欧拉回路,那么我们以哪一个字母为起点都可以,所以我们取字典序最小的字母作为起点,这样才能求得字典序最小的答案。

注意我们使用Hierholzers算法求出来的是倒序的,所以我们存的时候就可以直接倒着存n+1个字母,然后直接cout输出即可。

int Find(int x){if(fa[x] == x)return x;return fa[x] = Find(fa[x]);
}
void dfs(int x){for(int i = 65; i <= 125 ; ++ i){if(g[x][i]){g[x][i] -- , g[i][x] -- ;dfs(i);}}ans[tot -- ] = x;
}
//找欧拉路径
int main(){scanf("%d", &n);tot = n;for(int i = 1; i <= 500; ++ i)fa[i] = i;for(int i = 1; i <= n; ++ i){cin >> s;int x = s[0], y = s[1];g[x][y] ++ , g[y][x] ++ ;d[y] ++ , d[x] ++ ;int fx = Find(x), fy = Find(y);fa[fx] = fy;}int cnt = 0;for(int i = 65; i <= 125; ++ i){if(Find(i) == i && d[i])cnt ++ ;}if(cnt != 1){//多个祖先,说明有孤立点不是连通图。puts("No Solution");return 0;}cnt = 0;int root = 0;for(int i = 65; i <= 125; ++ i){if(d[i] & 1){if(root == 0)//找字典序最小的起点root = i;cnt ++ ;}}if(cnt && cnt != 2){//奇度点为0或者2才存在欧拉路径puts("No Solution");return 0;}if(root == 0){//存在欧拉回路,那么任意点都可以作为起点。所以找字典序最小的起点for(int i = 65; i <= 125; ++ i){if(d[i]){root = i;//找到字典序最小的起点break;}}}dfs(root);cout << ans <<endl;return 0;
}

FleuryFleuryFleury算法

void dfs( int u ) {sta.push(u);for( register int i = head[u]; i; i = line[i].nxt ) {if( vis[i] ) continue;vis[i] = vis[ i ^ 1 ] = true;dfs( line[i].to );break;}
}void fleury( int st ) {sta.push(st);while(!sta.empty() ) {int flag = 0, u = sta.top(); sta.pop();for( register int i = head[u]; i; i = line[i].nxt) {if( vis[i] ) continue;flag = 1; break;}if( !flag ) printf( "%d\n", u );else        dfs(u);}
}

究极优化版

给定一张图,请你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。
type为1时图是无向图。
type为0使图是有向图。

如果出题人丧心病狂(200000个1的自环),普通的代码T了可以用这个代码

const int N = 500010, M = 500007, INF = 0x3f3f3f3f;
int n, m;
int type;
int head[N], nex[M], edge[M], ver[M], tot;
int ans[M];
bool used[M];
int cnt;
int din[N], dout[N];void add(int x,int y){ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}void dfs(int x)
{//特殊数据O(n)优化,固定i引用变量,这样head[x]也会变,达到删边的效果for(int &i = head[x]; ~i;){if(used[i]){i = nex[i];//删边,先删再dfs,防止遍历到祖先节点时该边未被删去continue;}used[i] = true;if(type == 1)used[i ^ 1] = true;int t;if(type == 1){//无向图就是正反两条边t = i / 2 + 1;//实际上只有一条,我们建图的时候无向图建了两条if(i & 1)//奇数就是反向边,根据题意取负数t = -t;}//有向图就是下一条边else t = i + 1;int y = ver[i];i = nex[i];//删边dfs(y);ans[++cnt] = t;}
}
int main(){scanf("%d",&type);scanf("%d%d",&n,&m);memset(head,-1,sizeof head);for(int i = 1;i <= m;++i){int x,y;scanf("%d%d",&x,&y);add(x,y);if(type == 1)add(y,x);din[y] ++ , dout[x] ++ ;}//先判断不成立的情况if(type == 1){for(int i = 1;i <= n;++i)if((din[i] + dout[i]) & 1){/*求的是欧拉回路所以奇数度点要为0*/puts("NO");return 0;}}else {for(int i = 1;i <= n;++i){if(din[i] != dout[i]){puts("NO");return 0;}}}for(int i = 1;i <= n;++i)//找到一个可行的起点,因为本题中的1~n中的点可能没有连边if(head[i] != -1){dfs(i);break;}if(cnt < m){//如果不全部连通,则不存在欧拉路puts("NO");return 0;}puts("YES");for(int i = cnt;i;i -- )//倒序输出printf("%d ",ans[i]);puts("");return 0;
}

混合图欧拉回路

题目描述:给出一个 V(V<=100)个点和 E(E<=500)
条边的无向边与有向边的混合图,试打印出它的任意一条欧拉回路(无向边的两个方向只能从某个方向经过一次),如果没有输出 No euler circuit exist。输入保证图连通。

由于有向图有欧拉回路当且仅当图是弱连通的并且所有点的 入度-出度=0,可以发现关键问题在于如何给无向边定向使得入度-出度=0.
先给所有无向边任意指定一个方向,并令 d’[i]=i的出度-入度(算上原图的有向边和定向了的无向边)。 如果某个 d’[i]
是奇数(相当于说它连接了奇数条边,包括有向边和无向边),那么显然无解,否则令 d[i]=d’[i]/2(为了方便,暂时称其为“度”)。
考虑如果有一条边 u->v 在原图中是无向边,那么将它反向,变成 v->u 之后,u的出度-1,入度+1,从而
d[u]-=1;同理,d[v]+=1. 可以发现这相当于说把 u 的一个度“流到”了 v 的一个度。 于是我们(在网络流中)从 u 到 v
连一条容量为 1 的边,表示可以在 d[u] 中流出一个给 d[v]。 另外,对每个点 u,如果 d[u]>0,那么要从 S 到 u
连一条容量为 d[u] 的边,表示 u 这里多出了 d[u] 的度; 否则 d[u]<=0,就从 u 到 T 连一条容量为 -d[u]
的边,表示 u 这里缺少 -d[u] 的度。 执行一遍最大流之后如果 S
出发的所有弧都满流,就说明所有多出来的度都流出去了(同时所有少的度都补上了,因为少的度的总数 这时候只需要看网络流中哪些 u->v
的边流上了,就知道哪些边需要反向。把这些边反向之后找一遍有向图欧拉回路即可。

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <vector>typedef long long LL;int readInt() {int ans = 0, c, f = 1;while (!isdigit(c = getchar()))if (c == '-') f *= -1;do ans = ans * 10 + c - '0';while (isdigit(c = getchar()));return ans * f;
}const int N = 105;
const int M = 1500;int n, m, S, T, pre[N], nxt[M], to[M], ret[M], d[N], cnt, sum;
int u[M], v[M], id[M];
std::vector<int> V[N];inline void addEdge(int x, int y, int r) {nxt[cnt] = pre[x];ret[cnt] = r;to[pre[x] = cnt++] = y;nxt[cnt] = pre[y];ret[cnt] = 0;to[pre[y] = cnt++] = x;
}bool Init() {n = readInt(); m = readInt();S = 0; T = n + 1;memset(pre, -1, sizeof pre);cnt = 0;memset(d, 0, sizeof d);for (int i = 0; i < m; ++i) {u[i] = readInt(); v[i] = readInt();int c;while (!isalpha(c = getchar()));++d[u[i]]; --d[v[i]];if (c == 'U') id[i] = cnt, addEdge(u[i], v[i], 1);else id[i] = -1;}sum = 0;for (int i = 1; i <= n; ++i) {if (d[i] & 1) return false;d[i] /= 2;if (d[i] > 0) {addEdge(S, i, d[i]);sum += d[i];} else addEdge(i, T, -d[i]);}return true;
}int que[N], dis[N], cur[N];bool BFS() {int hd = 0, tl = 0;memset(dis, -1, sizeof dis);dis[que[tl++] = S] = 0;while (hd < tl)for (int x = que[hd++], i = pre[x]; ~i; i = nxt[i])if (ret[i] != 0 && dis[to[i]] == -1)dis[que[tl++] = to[i]] = dis[x] + 1;return dis[T] != -1;
}int DFS(int x, int maxf) {if (x == T) return maxf;int ans = 0;for (int &i = cur[x]; ~i; i = nxt[i])if (ret[i] != 0 && dis[to[i]] == dis[x] + 1) {int t = DFS(to[i], std::min(ret[i], maxf - ans));ret[i] -= t;ret[i ^ 1] += t;if ((ans += t) == maxf) break;}return ans;
}void Euler(int x) {while (!V[x].empty()) {int y = V[x].back(); V[x].pop_back();Euler(y);printf(" %d", x);}
}void Solve() {int ans = 0;while (BFS())memcpy(cur, pre, sizeof cur), ans += DFS(S, 0x7fffffff);if (ans != sum) puts("No euler circuit exist");else {for (int i = 1; i <= n; ++i) V[i].clear();for (int i = 0; i < m; ++i)if (id[i] >= 0 && !ret[id[i]])V[u[i]].push_back(v[i]);elseV[v[i]].push_back(u[i]);printf("1");Euler(1);puts("");}
}int main() {int T = readInt();while (T--) {if (!Init()) puts("No euler circuit exist");else Solve();if (T) puts("");}return 0;
}

模板 - 欧拉路、欧拉回路(一笔画问题)相关推荐

  1. 欧拉回路与欧拉路(模板)

    欧拉回路 欧拉图: 就是从任意一个点开始都可以一笔画完整个图 半欧拉图: 必须从某个点开始才能一笔画完整个图. 对于无向图 , 是欧拉图当且仅当 是连通的且没有奇度顶点. 对于无向图 , 是半欧拉图当 ...

  2. 图论--欧拉路,欧拉回路(小结)

    在题目中在慢慢细说概念 1.HDU - 3018 Ant Trip 题目大意:又N个村庄,M条道路.问须要走几次才干将全部的路遍历 解题思路:这题问的是有关欧拉路的判定 欧拉路就是每条边仅仅能走一次, ...

  3. 【Luogu1341】无序字母对(并查集联通,欧拉路模板)

    problem 给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒). 请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现. 输出字典序最小的方案(n的规模 ...

  4. 【原创】一笔画问题(欧拉路)

    一笔画问题(euler-circuit.cpp) 题目描述 对给定的一个无向图,判断能否一笔画出.若能,输出一笔画的先后顺序,否则输出"No Solution!" 所谓一笔画出,即 ...

  5. hrbust 哈理工oj 1633 word!word!【欧拉路、欧拉回路的有向图判断】

      word!word! Time Limit: 1000 MS Memory Limit: 32768 K Total Submit: 58(20 users) Total Accepted: 25 ...

  6. 欧拉路和欧拉回路知识

    概念 如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path). 如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit). [1] 具有欧拉回路的图称为欧拉图 ...

  7. HDOJ3018欧拉路,几笔画判断

    //并查集+简单的欧拉路条数判断 #include <iostream> #include <cstring> #include <cstdio> using na ...

  8. 图论-欧拉路(UVA10054)(HDU1116)

    首先说一下定义: 欧拉路:从图中某点出发可以遍历全图,图中的每条边通过且只能通过一次. 欧拉回路:具有欧拉路性质且起点位置与终点位置相同. 主要问题就是一个图中是否存在欧拉路,和打印欧拉路路径. 先说 ...

  9. 欧拉路HDU3018

    欧拉路,欧拉回路,讲的实际上就是一笔画的问题. 给定n个点,m条边,如果能一笔把所有边都连上就是欧拉路,如果起点和终点是同一点,就是欧拉回路. 欧拉路的特征:对于无向图,如果所有点的度都是偶数,那么任 ...

最新文章

  1. mysql5.7 sql监控_MySQL5.7中 performance和sys schema中的监控参数解释
  2. 广义线性模型、Logistic、sigmoid、softmax、多分类、与大数据、优缺点
  3. UC 伯克利华人一作:卷积让视觉 Transformer 性能更强,ImageNet 继续刷点!
  4. 项目Alpha冲刺--5/10
  5. 水鱼 学习回顾 <1>
  6. MindCon极客周 | 第三届全新席卷而来,SIG英雄帖,做硬核玩家,夺万元大奖!
  7. Spring / Hibernate使用log4jdbc改进了SQL日志记录
  8. 二叉树题目----6 二叉树的最近公共祖先 AND 二叉树搜索树转换成排序双向链表
  9. leetcode 82. 删除排序链表中的重复元素 II(map)
  10. 《哪吒之魔童降世》电影密钥延期至9月26日,将冲击中国票房总榜前三
  11. PYTHON开发--面向对象基础入门
  12. 【★★★★★】提高PHP代码质量的36个技巧
  13. java面试题-基础篇(万字总结,带答案,面试官问烂,跳槽必备)
  14. 感谢《蜗居》中的100句经典台词让我们提前认清了现实[转帖]
  15. 【RPC Dubbo】本地存根和本地伪装
  16. PDF如何解密?这些方法你知道吗?
  17. Debian参考手册读书摘要
  18. 阿里acp认证是什么 阿里acp认证含金量高吗
  19. 全国第17届计算机辅助设计与图形学(cad/cg)学术会议论文集,征稿资讯-CCF第24届全国计算机辅助设计与图形学学术会议 (CCF CAD/CG 2021)...
  20. 小型微型计算机系统加急,小型微型计算机系统杂志

热门文章

  1. 一文读懂图像局部特征点检测算法
  2. 综述 | 基于深度学习的目标检测算法
  3. 将项目通过git上传到bitbucket仓库 (附图)
  4. MySQL 里的 Timestrap 和 DateTime 和 Java 中的 Date
  5. web初级开发的那些坑
  6. Luke 5—— 可视化 Lucene 索引查看工具,可以查看ES的索引
  7. DT时代下[个推3.0]遵循的四个法则
  8. Java局部变量final
  9. android ORM框架LitePal 封装SQLite
  10. Codeforces Gym 100513G G. FacePalm Accounting 暴力