圖論算法

目錄

並查集

最小生成樹(克魯斯卡爾)

最短路(Dijkstra)

最短路(SPFA)

最短路(Floyd)

拓撲排序

歐拉路徑


並查集

int fa[MAX];

void init(){
  for(int i=0;i<MAX;i++)fa[i]=i;
}

int find(int i){
  int temp=i;
  while(temp!=fa[temp])temp=fa[temp];
  if(temp!=i){
    int t=fa[i];
    fa[i]=temp;
    i=t;
  }
  return temp;
}

void merge(int a,int b){
  fa[a]=b;
}

最小生成樹(克魯斯卡爾)

struct edge {
  int u,v,w;
};

int n,m,a,b,c;
vector<edge> v;

bool cmp(edge a,edge b) {
  return a.w<b.w;
}

int kruskal(){
  init(); // 初始化並查集
  int ans=0;
  sort(v.begin(),v.end(),cmp); // 按照邊權值的大小排序
  for(int i=0; i<v.size(); i++) { // 依次選擇權值小的邊
    int a=find(v[i].u);
    int b=find(v[i].v);
    if(a!=b) { // 判斷這兩個點是否已經連接
      merge(a,b);
      ans+=v[i].w;
    }
  }
  return ans;
}

最短路(Dijkstra)

int vis[MAX],d[MAX];
int w[MAX][MAX];

// 以鄰接矩陣存儲圖
void dijkstra(){
  memset(vis,0,sizeof(vis)); // 初始化vis數組
  for(int i=1;i<=n;i++)d[i]=w[1][i]; // 初始化到起點的距離
  vis[1]=1;
  for(int i=1;i<=n;i++){
    int minn=INF,k=0;
    for(int j=1;j<=n;j++) // 尋找當前路徑最短的點
      if(!vis[j] && d[j]<minn)
        minn=d[j],k=j;
    vis[k]=1;
    for(int j=1;j<=n;j++) // 鬆弛操作
      if(!vis[j] && minn+w[k][j]<d[j])d[j]=minn+w[k][j];
  }
}

最短路(SPFA)

struct node { // 連接的點和到達該點的時間
  int to,time;
};

vector<node> G[MAX];

int n,m,a,b,c;
int vis[MAX],d[MAX];

// 以鄰接表存儲圖
void spfa() {
  memset(vis,0,sizeof(vis)); // 初始化vis數組
  for(int i=0; i<MAX; i++) d[i]=INF; // 初始化距離最大
  queue<node> q;
  q.push({1,0}); // 起點入列
  vis[1]=1; // 進入隊列標誌
  d[1]=0; // 起點距離爲0
  while(!q.empty()) {
    node u=q.front(); q.pop();
    vis[u.to]=0; // 出隊列標誌
    for(int i=0; i<G[u.to].size(); i++) { // 遍歷所有鄰接的點
      node v=G[u.to][i];
      if(d[v.to]>d[u.to]+v.time) { // 鬆弛操作
        d[v.to]=d[u.to]+v.time;
        if(!vis[v.to]) { // 如果沒有入列則入列
          vis[v.to]=1;
          q.push(v);
        }
      }
    }
  }
}

最短路(Floyd)

int w[MAX][MAX];

// 以鄰接矩陣存儲圖
void floyd(){
  for(int k=1;k<=n;k++){ // 作爲中間節點
    for(int i=1;i<=n;i++){
      if(w[i][k]!=INF){
        for(int j=1;j<=n;j++){
          w[i][j]=min(w[i][j],w[i][k]+w[k][j]); // 鬆弛操作
        }
      }
    }
  }
}

拓撲排序

int w[MAX][MAX];
int rd[MAX]; // 存儲結點的入度
int vis[MAX];
int topo[MAX]; // 拓撲排序後的順序

// 以鄰接矩陣存儲圖
bool toposort(){
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      if(!vis[j]&&rd[j]==0){ // 沒有遍歷過且入度爲0
        vis[j]=1;
        topo[i]=j;
        for(int k=1;k<=n;k++) // 刪除以這個點爲弧尾的邊,並將入度減1
          if(w[j][k]){w[j][k]=0;rd[k]--;}
        break;
      }
    }
  }
  for(int i=1;i<=n;i++) // 如果有節點沒有遍歷到,說明有環
    if(!vis[i])return 0;
  return 1;
}

歐拉路徑

int g[MAX][MAX]; // 鄰接矩陣存儲圖,元素爲結點間邊的數量
int d[MAX]; // 存儲結點的度
int path[MAX],p=0; // 存儲歐拉路徑

// 無向圖歐拉路徑
bool eulor(){
  int cnt=0;
  for(int i=1;i<=n;i++) // 並查集判斷圖是否連通
    if(fa[i]==i)cnt++;
  if(cnt!=1)return 0;
  cnt=0;
  for(int i=1;i<=n;i++) // 無向圖判斷是否只有兩個度爲奇數的結點(歐拉通路)
    if(d[i]%2==1)cnt++; // 或者無度爲奇數的結點(歐拉回路)
  if(cnt==0||cnt==2)return 1;
  return 0;
}

// dfs求歐拉路徑
void dfs(int start){ // 給予歐拉路徑起始點
  for(int j=1;j<=n;j++){
    if(g[start][j]){
      g[start][j]--;g[j][start]--;
      dfs(j);
      if(g[start][j])j--;
    }
  }
  path[p++]=start; // 存儲歐拉路徑
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章