1. 程式人生 > >模板庫(三) - 圖論演算法模板

模板庫(三) - 圖論演算法模板

寫在前面

模板庫”這一系列文章用來複習 O I OI 模板
由於時間原因,作者無法一一親自除錯其中的程式,也因如此,有一部分程式來自於網際網路,如果您覺得這侵犯了您的合法權益,請聯絡 ( Q

Q 2068926345 ) (QQ2068926345) 刪除。
對於給您造成的不便和困擾,我表示深深的歉意。
本系列文章僅用於學習,禁止任何人或組織用於商業用途。
本系列文章中,標記*的為選學演算法,在 N O
I P NOIP
中較少涉及。

圖論演算法模板

圖的遍歷和儲存

【簡介】

圖是計算機內部一種常見的資料結構,分為無向圖和有向圖。
在無向圖中,若圖中任意一對頂點都是連通的,則稱此圖是連通圖。
在有向圖中,若任意一對頂點 u u

v v 間存在一條從 u u v v 的路徑和從 v v u u 的路徑,則稱此圖是強連通圖。
無向圖的一個極大連通子圖稱為該圖的一個連通分量。
有向圖的一個極大強連通子圖稱為該圖的一個強連通分量。
在圖的每條邊上加上一個數字作權,也稱代價,帶權的圖稱為網。

鄰接矩陣存圖與遍歷

#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; 
}

複雜度 Θ ( n 2 ) Θ(n^2)

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; 
}

複雜度 Θ ( n ) Θ(n) ,常數略大

鄰接表存圖與遍歷

#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;
}

複雜度 Θ ( n ) Θ(n)


一筆畫問題

【簡介】

如果一個圖存在一筆畫,則一筆畫的路徑叫做尤拉路,如果最後又回到起點,那這個路徑叫做歐拉回路。
  我們定義奇點是指跟這個點相連的邊數目有奇數個的點。對於能夠一筆畫的圖,我們有以下兩個定理。
   定理 1 1 :存在尤拉路的條件:圖是連通的,有且只有 2 2 個奇點。
   定理 2 2 :存在歐拉回路的條件:圖是連通的,有 0 0 個奇點。
  兩個定理的正確性是顯而易見的,既然每條邊都要經過一次,那麼對於尤拉路,除了起點和終點外,每個點如果進入了一次,顯然一定要出去一次,顯然是偶點。對於歐拉回路,每個點進入和出去次數一定都是相等的,顯然沒有奇點。
  求尤拉路的演算法很簡單,使用深度優先遍歷即可。
  根據一筆畫的兩個定理,如果尋找歐拉回路,對任意一個點執行深度優先遍歷;找尤拉路,則對一個奇點執行 D F S DFS

【程式碼實現】
#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;
}

複雜度 Θ ( n + m ) Θ(n+m)


哈密頓迴路問題

【簡介】

歐拉回路是指不重複地走過所有路徑的迴路,而哈密爾頓環是指不重複地走過所有的點,並且最後還能回到起點的迴路。

【程式碼實現】
#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]<