模板庫(三) - 圖論演算法模板
寫在前面
“模板庫”這一系列文章用來複習
模板
由於時間原因,作者無法一一親自除錯其中的程式,也因如此,有一部分程式來自於網際網路,如果您覺得這侵犯了您的合法權益,請聯絡
刪除。
對於給您造成的不便和困擾,我表示深深的歉意。
本系列文章僅用於學習,禁止任何人或組織用於商業用途。
本系列文章中,標記*的為選學演算法,在
中較少涉及。
圖論演算法模板
圖的遍歷和儲存
【簡介】
圖是計算機內部一種常見的資料結構,分為無向圖和有向圖。
在無向圖中,若圖中任意一對頂點都是連通的,則稱此圖是連通圖。
在有向圖中,若任意一對頂點
和
間存在一條從
到
的路徑和從
到
的路徑,則稱此圖是強連通圖。
無向圖的一個極大連通子圖稱為該圖的一個連通分量。
有向圖的一個極大強連通子圖稱為該圖的一個強連通分量。
在圖的每條邊上加上一個數字作權,也稱代價,帶權的圖稱為網。
鄰接矩陣存圖與遍歷
#include<cstdio>
#include<vector>
using namespace std;
#define N 1001
bool vis[N];
int n,m,ans[N],g[N][N];
void dfs(int now,int start){
vis[now]=true;ans[now]=start;
for(int i=1;i<=n;i++)
if(!vis[i]&&g[now][i]) dfs(i,start);
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
g[y][x]=1;
}
for(int i=n;i>=1;i--)if(!vis[i]) dfs(i,i);
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
複雜度
Vector存圖與遍歷
#include<cstdio>
#include<vector>
using namespace std;
#define N 100001
vector<int> g[N];
bool vis[N];
int n,m,ans[N];
void dfs(int now,int start){
vis[now]=true;ans[now]=start;
for(int i=0;i^g[now].size();i++)
if(!vis[g[now][i]]) dfs(g[now][i],start);
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
g[y].push_back(x);
}
for(int i=n;i>=1;i--)if(!vis[i]) dfs(i,i);
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
複雜度 ,常數略大
鄰接表存圖與遍歷
#include<cstdio>
struct Edge{
int next,to;
}edge[200007];
int n,m,head[100007],tot;
bool vis[100007];
inline void add_edge(int from,int to){
edge[++cnt].next=head[from];
edge[cnt].to=to;head[from]=cnt;
}
int ans[N];
void dfs(int now,int start){
vis[now]=true;ans[now]=start;
for(int i=head[now];i;i=edge[i].nxt)
if(!vis[edge[i].to]) dfs(edge[i].to,start);
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add_edge(y,x);
}
for(int i=n;i>=1;i--)if(!vis[i]) dfs(i,i);
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
複雜度
一筆畫問題
【簡介】
如果一個圖存在一筆畫,則一筆畫的路徑叫做尤拉路,如果最後又回到起點,那這個路徑叫做歐拉回路。
我們定義奇點是指跟這個點相連的邊數目有奇數個的點。對於能夠一筆畫的圖,我們有以下兩個定理。
定理
:存在尤拉路的條件:圖是連通的,有且只有
個奇點。
定理
:存在歐拉回路的條件:圖是連通的,有
個奇點。
兩個定理的正確性是顯而易見的,既然每條邊都要經過一次,那麼對於尤拉路,除了起點和終點外,每個點如果進入了一次,顯然一定要出去一次,顯然是偶點。對於歐拉回路,每個點進入和出去次數一定都是相等的,顯然沒有奇點。
求尤拉路的演算法很簡單,使用深度優先遍歷即可。
根據一筆畫的兩個定理,如果尋找歐拉回路,對任意一個點執行深度優先遍歷;找尤拉路,則對一個奇點執行
。
【程式碼實現】
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<stack>
using namespace std;
const int maxn = 1031;
int G[maxn][maxn], du[maxn];
int n,m;
stack<int> S;//用一個棧來儲存路徑
void dfs(int u){
for(int v=1; v<=n; v++)
if(G[u][v]){
G[u][v]--,G[v][u]--;
dfs(v);
}
S.push(u);
}
int main(){
cin>>m;
int u,v;
for(int i=1; i<=m; i++){
cin>>u>>v;
n = max(n,max(u,v));
G[u][v]++,G[v][u]++;
du[u]++,du[v]++;
}//用鄰接矩陣記錄圖
int s = 1;
for(int i=1; i<=n; i++)
if(du[i]%2==1){
s=i;
break;
}//尋找起點
dfs(s);
while(!S.empty()){
cout<<S.top()<<endl;
S.pop();
}
return 0;
}
複雜度
哈密頓迴路問題
【簡介】
歐拉回路是指不重複地走過所有路徑的迴路,而哈密爾頓環是指不重複地走過所有的點,並且最後還能回到起點的迴路。
【程式碼實現】
#include<bits/stdc++.h>
#define max(a,b) (a>b?a:b)
using namespace std;
typedef long long(LL);
typedef unsigned long long(ULL);
const double eps(1e-8);
char B[1<<15],*S=B,*T=B,ch;
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
int aa,bb; int F(){
while(ch=getc(),(ch<'0'||ch>'9')&&ch!='-'); ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
while(ch=getc(),ch>='0'&&ch<='9')aa=aa*10+ch-'0'; return bb?aa:-aa;
}
#define N 100010
int n,swp,cnt,z[N]; long long ans;
#define swap(a,b) (swp=a,a=b,b=swp)
#define abs(x) (x>0?x:-(x))
#define max(a,b) (a>b?a:b)
#define cmax(x) (ans<x?ans=x:1)
struct P {int x,y,id,nx,ny;} p[N];
bool operator<(const P&a,const P&b) {return a.nx<b.nx||a.nx==b.nx&&a.ny<b.ny;}
class Graph{
private:
int et,la[N],ufs[N],tot;
struct D{
int x,y,v;
bool operator<(const D&a)const {return v<a.v;}
} d[N<<2];
struct E {int to,v,nxt;} e[N<<1];
int gf(int x) {return ufs[x]==x?x:ufs[x]=gf(ufs[x]);}
void adde(int x,int y,int v){
e[++et]=(E) {y,v,la[x]},la[x]=et;
e[++et]=(E) {x,v,la[y]},la[y]=et;
}
public:
Graph() {et=1;}
void add(int x,int y,int v) {d[++tot]=(D) {x,y,v};}
void make(){
std::sort(d+1,d+1+tot);
for(int i=1; i<=n; i++)ufs[i]=i; cnt=n;
for(int i=1,x,y; i<=tot; i++)
if((x=gf(d[i].x))!=(y=gf(d[i].y))){
ufs[x]=y,cnt--,ans+=d[i].v,
adde(d[i].x,d[i].y,d[i].v);
}
}
} G;
struct D {int x,n;} d[N];
bool operator<(const D&a,const D&b) {return a.x<b.x;}
#define dis(i,j) (abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y))
void ins(int i){
for(int t=p[i].ny; t<=cnt; t+=t&-t)
if(z[t]==0||p[z[t]<