本文主要內容是歐拉路徑和歐拉回路。
概念
- 歐拉路徑:不重複地經過每條邊的路徑
- 歐拉回路:不重複地經過每條邊的迴路
- 歐拉圖:存在歐拉回路的圖
- 半歐拉圖:存在歐拉路徑的圖
定理
- 無向圖爲歐拉圖的充要條件:連通且沒有奇數度數的點(簡稱奇點)。
- 無向圖爲半歐拉圖的充要條件:連通且奇點數爲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