本文主要内容是欧拉路径和欧拉回路。
概念
- 欧拉路径:不重复地经过每条边的路径
- 欧拉回路:不重复地经过每条边的回路
- 欧拉图:存在欧拉回路的图
- 半欧拉图:存在欧拉路径的图
定理
- 无向图为欧拉图的充要条件:连通且没有奇数度数的点(简称奇点)。
- 无向图为半欧拉图的充要条件:连通且奇点数为2。(这两个点的分别是起点和终点)
- 有向图为欧拉图的充要条件:基图连通且所有点入度等于出度。
- 有向图为半欧拉图的充要条件:基图连通且存在一点s入度比出度少一,另一点t入度比出度多一,其余点入度等于出度。(s和t分别是起点和终点)
其他定理1
- 当一连通无向图奇数度数的点数k>2时,需要笔画成。(k一定为偶数)
- 可以通过加边的方式将非欧拉图改成欧拉图。对于无向图,每个奇点加一度,最终加边数为;对于有向图,每个点加对应数目的入度或出度使该点入度等于出度,最少加边数是。
应用
1.判断一个无向图是否为欧拉图
题目:HDU1878欧拉回路
注意,一定要判是否连通
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n, m;
int du[N], fa[N];
int getfa(int u)
{
if(fa[u]==u) return u;
return fa[u]=getfa(fa[u]);
}
void merge(int u, int v)
{
int fu=getfa(u), fv=getfa(v);
if(fu!=fv) fa[fu]=fv;
return;
}
int main()
{
while(~scanf("%d", &n) && n)
{
scanf("%d", &m);
memset(du,0,sizeof(du));
for(int i=1; i<=n; ++i) fa[i]=i;
for(int i=0, u, v; i<m; ++i)
{
scanf("%d%d", &u, &v);
du[u]++; du[v]++;
merge(u,v);
}
bool ok=true;
int t;
// 找到一个有边相连的点的 fa
for(int i=1; i<=n; ++i)
if(du[i])
t=fa[i];
// 欧拉图的条件是所有点的度数为偶数,且所有边连在一起
for(int i=1; i<=n; ++i)
if(du[i]&&(du[i]&1||getfa(i)!=t)) ok=false;
puts(ok?"1":"0");
}
return 0;
}
2.输出半欧拉图字典序最小的欧拉路径
洛谷P2731骑马修栅栏
先dfs,再倒序输出经过的每个点。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=500, M=1030;
int m, sn;
int cnt[N+10][N+10], st[M], du[N+10];
void dfs(int u)
{
for(int i=1; i<=N; ++i)
{
if(cnt[u][i]==0) continue;
cnt[u][i]--; cnt[i][u]--;
dfs(i);
}
st[++sn]=u;
}
int main()
{
scanf("%d", &m);
int s=N;
for(int i=0, u, v; i<m; ++i)
{
scanf("%d%d", &u, &v);
cnt[u][v]++; cnt[v][u]++;
du[u]++; du[v]++;
s=min(s,u); s=min(s,v);
}
for(int i=1; i<=N; ++i)
if(du[i]&1)
{
s=i;
break;
}
dfs(s);
for(int i=sn; i; --i)
printf("%d\n", st[i]);
return 0;
}
3.求无向图中的欧拉回路2
我暂时还不会这个
4.一道有难度的例题
英文原题 Maximum Matching
中文翻译 最大匹配
英文题解
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int S=16, N=110;
#define compress(i,j) ((i)<(j)?(i)*4+(j):(j)*4+(i))
struct Edge
{
int c1, val, c2, id;
bool operator<(const Edge & t)const{ return val<t.val; }
}e[N<<1];
struct Node
{
int to, val, id;
};
struct UnionFind
{
int f, val;
}u[4];
vector<Node> V[4];
vector<Edge> E[S];
int n;
int cnt[4];
bool vis[N], have[4];
int findfa(int a)
{
if(u[a].f==a) return a;
return u[a].f=findfa(u[a].f);
}
void merge(int a, int b, int val)
{
int fa=findfa(a), fb=findfa(b);
if(fa!=fb) u[u[fa].f=fb].val+=u[fa].val+val;
else u[fb].val+=val;
return;
}
int getans(int i)
{
int f1=findfa(i), maxv=0, numj=0;
for(int i=0; i<4; ++i)
if(findfa(i)==f1)
{
have[i]=true;
maxv=max(maxv,u[i].val);
if(cnt[i]&1) numj++;
}
if(numj==2||numj==0) return maxv;
return 0;
}
int main()
{
int ans=0;
// input
scanf("%d", &n);
for(int i=1; i<=n; ++i)
{
int c1, val, c2, id;
scanf("%d%d%d", &c1, &val, &c2);
c1--; c2--;
V[c1].push_back((Node){c2,val,i});
V[c2].push_back((Node){c1,val,i});
e[i]=(Edge){c1,val,c2,i};
E[compress(c1,c2)].push_back(e[i]);
}
for(int i=0; i<S; ++i)
{
if(E[i].size())
{
sort(E[i].begin(),E[i].end());
}
}
//solve
for(int s=0; s<1<<S; ++s)
{
//initialization
memset(vis,false,sizeof(vis));
memset(cnt,0,sizeof(cnt));
memset(have,false,sizeof(have));
for(int i=0; i<4; ++i)
{
u[i].f=i; u[i].val=0;
}
//union find
for(int i=0; i<S; ++i)
if((s&(1<<i))&&E[i].size())
vis[E[i][0].id]=1;
for(int i=1; i<=n; ++i)
if(!vis[e[i].id])
{
int a=e[i].c1, b=e[i].c2;
merge(a,b,e[i].val);
cnt[a]++; cnt[b]++;
}
// get ans
for(int i=0; i<4; ++i)
if(!have[i])
ans=max(ans,getans(i));
}
printf("%d\n", ans);
return 0;
}
最后,武汉加油,一定可以战胜疫情的!
2020.01.27