欧拉图:
在图为连通图的前提下,欧拉通路:当前图中经过每条边一次且仅一次,若最终回到出发点则称为欧拉回路。
相关判定条件(图联通)
(1)无向图存在欧拉回路的条件是:图中不存在奇度结点,有向图存在欧拉回路的条件是:每个结点出度均等于入度。
(2)无向图存在欧拉通路的条件是:图中仅存在两个奇度结点(起点和终点),有向图存在欧拉通路的条件是:存在两个结点入度不等于出度,分别为起点(出度-入度==1)和终点(入度-出度==1)
除此之外个人认为无向图只要存在欧拉回路则从任意一点出发均可遍历全部边回到该点(哈密顿图同理)。
判定欧拉图只是基础,更重要的是找出具体的欧拉路径(回路)
算法:
使用链式前向星存储图(便于找边),可先通过并查集(或者广搜)判断当前图是否连通,然后还能记录出入度根据之前定理判定。然后通过深搜并且标记每一条走过的边确定是否有欧拉路径(回路),算法整体思路较简单,关键是能否想到利用欧拉路径(回路)建立模型。
哈密顿图:
在图为无向连通图的前提下,哈密顿通路:经过每个顶点一次且仅一次,若最终回到出发点则称为哈密顿图,哈密顿图为NP完全问题,暂不存在多项式时间内的解法
相关判定条件(图无向且连通)
(1)若图有n个结点的无向图且任意两个不同的结点度数之和均大于n,则此图为哈密顿图(存在哈密顿回路),此为判断是否是哈密顿图的充分条件,也就是说满足这个条件一定是哈密顿图,但哈密顿图不一定必满足这个条件。
算法:(前提是图有n个结点的无向图且任意两个不同的结点度数之和均大于n)
选取任意结点作为S,与其相邻一结点为T,然后逐步向两边扩展至无法扩展为止,若此时S与T连通,则判断是否S->T中包含全部点,若不连通则查找S->T中结点v[i]与T相邻且v[i+1]与S相邻的点,即S->v[i]->v[i+1]->T,而此时S ->V[i+1],T->V[i],将V[i+1]及其后部分翻转可构成S->v[i]->T->v[i+1]的哈密顿回路,判断此时是否路径包含全部结点,若不包含,查找不属于回路中但与其中结点相邻的结点,然后可将此回路缩为一点(因为已经求得当前回路为哈密顿回路)再修改S、T重复上述过程。
欧拉路径(回路)相关题目:UVA10054、POJ2230、HDU1116
哈密顿(回路)相关题目:POJ2438
UVA10054(每一个珠子首尾连接构成无向图)
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1010; const int maxm = maxn*maxn; struct node { int u, v; int next; }mapp[maxm]; int head[maxn]; int id; int n, m; bool visited[maxm]; node ans[maxm]; int tot; int root[maxn]; void init() { memset(head, -1, sizeof(head)); id = 0; memset(visited, false, sizeof(visited)); tot = 0; } void addedge(int u, int v) { mapp[id].u = u, mapp[id].v = v, mapp[id].next = head[u], head[u] = id ++; mapp[id].u = v, mapp[id].v = u, mapp[id].next = head[v], head[v] = id ++; } int find(int x) { if (x == root[x])return x; root[x] = find(root[x]); return root[x]; } void merge(int x, int y) { int xx = find(x), yy = find(y); if (xx > yy)root[xx] = yy; else root[yy] = xx; } void eule(int x) { for (int j = head[x]; j != -1; j = mapp[j].next){ if (!visited[j]){ visited[j] = true; visited[j^1] = true; eule(mapp[j].v); ans[tot ++] = mapp[j];//记录欧拉路径 } } } int main() { int cas, nc = 0; scanf("%d", &cas); while (cas --){ scanf("%d",&m); int a,b, t = m; init(); int st = 1<<30, ed = -1; int in[maxn]; memset(in, 0, sizeof(in)); for (int i = st; i <= ed; i ++) root[i] = i; while (m --){ scanf("%d%d", &a, &b); merge(a, b);//利用并查集判断是否为联通图 st = min(a, min(b, st)); ed = max(a, max(b, st)); in[a] ++, in[b] ++;//判断是否存在度数为奇数的结点 addedge(a, b); } bool istrue = true; for (int i = st; i <= ed; i ++){ if (in[i] % 2){ istrue = false; break; } } if (istrue){ for (int i = st; i < ed; i ++){ if (root[i] != root[i+1]){ istrue = false; break; } } } if (nc)printf("\n"); printf("Case #%d\n", ++nc); if (!istrue){ printf("some beads may be lost\n"); continue; } eule(st); for (int i = 1; i < tot; i ++){ if (ans[i-1].v != ans[i].u){ swap(ans[i].u, ans[i].v); } } if (ans[tot-1].v != ans[0].u){ swap(ans[0].u, ans[0].v); for (int i = 1; i < tot; i ++){ if (ans[i-1].v != ans[i].u){ swap(ans[i].u, ans[i].v); } } } bool isjudge = true; for (int i = 1; i < tot; i ++){ if (ans[i-1].v != ans[i].u){ isjudge = false; break; } } if (ans[tot-1].v != ans[0].u)isjudge = false; if (tot != t || ans[tot-1].v != ans[0].u || !isjudge){ printf("some beads may be lost\n"); continue; } for (int i = 0; i < tot; i ++)printf("%d %d\n", ans[i].u, ans[i].v); } return 0; }
HDU1116(将每一个单词首尾连接构成图,看是否存在欧拉路径,注意有回路的特殊情况以及输出格式要求)
#include <iostream> #include <queue> using namespace std; const int maxn = 30; const int maxm = 100100; struct edge { int v; int next; }mapp[maxm]; int id, n; bool visited[maxm]; int head[maxn]; int ans; bool is_true; void init() { memset(visited, false, sizeof(visited)); memset(head, -1, sizeof(head)); id = 0; } void addedge(int u, int v) { mapp[id].v = v, mapp[id].next = head[u], head[u] = id ++; } int cmp; bool bfs(int st)//宽搜判断是否为连通图 { queue<int> Que; bool visited[maxn]; memset(visited, false, sizeof(visited)); while (!Que.empty())Que.pop(); Que.push(st); int tot = 1; while (!Que.empty()){ int pre = Que.front(); Que.pop(); visited[pre] = true; for (int i = head[pre]; i != -1; i = mapp[i].next){ if (visited[mapp[i].v])continue; visited[mapp[i].v] = true;//注意这里的标记修改 tot ++; Que.push(mapp[i].v); } } return (tot == cmp) ? 1:0; } int main() { int t; char a[1100]; scanf("%d", &t); while (t --){ scanf("%d", &n); init(); ans = 0; int st = 28, ed = -1; int in[maxn], out[maxn]; memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); for (int i = 0; i < n; i ++){ scanf("%s", a); addedge(a[0]-'a', a[strlen(a)-1]-'a'); in[a[strlen(a)-1]-'a'] ++; out[a[0]-'a'] ++; st = min(st, min(a[0]-'a', a[strlen(a)-1]-'a')); ed = max(ed, max(a[0]-'a', a[strlen(a)-1]-'a')); } cmp = 0; is_true = true; int onum = 0; int st1, ed1; st1 = ed1 = -1; for (int i = st; i <= ed; i ++){ if (!(in[i]+out[i]))continue; cmp ++; if ((in[i]+out[i])%2){ onum ++; if (in[i] - out[i] == 1) ed1 = i; if (out[i] - in[i] == 1) st1 = i; if (onum > 2){ is_true = false; break; } } } if (!(ed1!=-1&&st1!=-1) || onum != 2)is_true = false; if (!is_true && bfs(st) && onum == 0){ bool istrue = true; for (int i = st; i <= ed; i ++){ if (in[i] + out[i] == 0)continue; if (in[i] != out[i]){ istrue = false; break; } } is_true = istrue; } is_true?printf("Ordering is possible.\n"):printf("The door cannot be opened.\n"); } return 0; }
POJ2438(纯裸的哈密顿图,注意是建立反图后求哈密顿回路)
#include <iostream> using namespace std; const int size = 410; int n, m; int a, b; int mapp[size][size]; bool visited[size]; void rever(int *ans, int i, int j) { while (j - i >= 1){ swap(ans[i], ans[j]); i ++, j --; } } void hanmi(int *ans, int &tot) { memset(visited, false, sizeof(visited)); int s = 1, t, i, j; for (i = 1; i <= 2*n; i ++){ if (mapp[s][i]){ t = i; break; } } tot = 2; ans[0] = s, ans[1] = t; visited[s] = visited[t] = true; while (1){ while (1){ for (j = 1; j <= 2*n; j ++){ if (mapp[t][j] && !visited[j]){ ans[tot ++] = j; visited[j] = true; t = j; break; } } if (j > 2*n)break; } rever(ans, 0, tot-1); swap(s,t); while (1){ for (j = 1; j <= 2*n; j ++){ if (mapp[t][j] && !visited[j]){ ans[tot ++] = j; visited[j] = true; t = j; break; } } if (j > 2*n)break; } if (!mapp[s][t]){//若当前st不构成回路 for (i = 1; i < tot-2; i ++){ if (mapp[ans[i+1]][s] && mapp[ans[i]][t]){ break; } } i ++; t = ans[i]; rever(ans, i, tot-1); } if (tot == 2*n)return ; for (j = 1; j <= 2*n; j ++){ if (visited[j])continue; for (i = 1; i < tot-1; i ++){ if (mapp[ans[i]][j])break; } if (mapp[ans[i]][j])break; } rever(ans, 0,i-1); s = ans[0]; rever(ans, i, tot-1); ans[tot ++] = j; t = j; visited[j] =true; } } int main() { int ans[size]; while (scanf("%d%d", &n, &m) != EOF && (m+n)){ memset(mapp, 0, sizeof(mapp)); while (m --){ scanf("%d%d", &a, &b); mapp[a][b] = mapp[b][a] = 1; } for (int i = 1; i <= 2*n; i ++){ for (int j = 1; j < i; j ++){ mapp[i][j] ^= 1; mapp[j][i] ^= 1; } } int tot = 0; hanmi(ans, tot); for (int i = 0; i < tot; i ++)printf("%d ", ans[i]); printf("\n"); } return 0; }