模板(图论)

图论:

拓扑排序

并查集

最大二分匹配(匈牙利算法)

二分图最大权匹配(KM算法)

有向图的强连通分量

最短路径算法:(dijkstra, 优先队列优化dijkstra, bellman-ford, SPFA)

Floyd算法

最小生成树:prim

次小生成树



[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /*  
  2.  *  拓扑排序; 
  3.  *  排序的结果从小到大, G[i][j]存储i-j的关系,(0无关系, 1表示存在关系) 
  4.  *  d[i]表示i节点的入度数; 
  5.  */  
  6. int n, G[maxn][maxn], topo[maxn], d[maxn];     
  7.   
  8. int toposort(){  
  9.     int flag = 1; // flag = 1: 有序, flag = -1: 结果不唯一;  
  10.     memset(d, 0, sizeof(d));  
  11.     for(int i = 0; i < n; ++i)  
  12.         for(int j = 0; j < n; ++j) d[i]+=G[j][i];  
  13.     for(int k = 0; k < n; ++k){  
  14.         int num = 0, p = -1;  
  15.         for(int i = 0; i < n; ++i) if(d[i] == 0) { num++; p = i; }  // 如果需要顺序输出, 遇到入度为0,直接退出;  
  16.         if(num == 0) return 0; // 有环;  
  17.         if(num > 1) flag = -1;  
  18.         topo[k] = p;  
  19.         d[p] = -1;  
  20.         for(int i = 0; i < n; ++i)  
  21.             if(G[p][i] == 1) d[i]--;  
  22.     }  
  23.     return flag;  
  24. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  *并查集 
  3.  *时间复杂度O(M*Alpha(N)) M次查找, 近似为O(M); 
  4.  */  
  5. // 并查集 注意初始化for(int i = 0; i < n; ++i) p[i] = i;  
  6. int Find(int x){  
  7.     return x == p[x] ? x : p[x] = Find(p[x]);  
  8. }  
  9.   
  10. void Union(int x, int y){  
  11.     int xRoot = Find(x), yRoot = Find(y);  
  12.     p[xRoot] = yRoot;  
  13. }  



[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /*  
  2.  *   匈牙利算法; 
  3.  */   
  4. int n, k, G[maxn][maxn], linker[maxn];  
  5. bool used[maxn];  
  6.   
  7. bool dfs(int u){  
  8.     for(int v = 0; v < n; ++v){  
  9.         if(G[u][v] && !used[v]){  
  10.             used[v] = true;  
  11.             if(linker[v] == -1 || dfs(linker[v])){  
  12.                 linker[v] = u;  
  13.                 return true;  
  14.             }  
  15.         }  
  16.     }  
  17.     return false;  
  18. }  
  19.   
  20. int hungary(){  
  21.     int res = 0;  
  22.     memset(linker, -1, sizeof(linker));  
  23.     for(int u = 0; u < n; ++u){  
  24.         memset(used, 0, sizeof(used));  
  25.         if(dfs(u)) res++;  
  26.     }  
  27.     return res;  
  28. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * KM算法 
  3.  * 复杂度O(N^3); 
  4.  */  
  5. int n, G[maxn][maxn], linker[maxn], lx[maxn], ly[maxn];  
  6. int slack[maxn];  
  7. bool visx[maxn], visy[maxn];  
  8.   
  9. bool dfs(int x){  
  10.     visx[x] = true;  
  11.     for(int y = 0; y < n; ++y){  
  12.         if(visy[y]) continue;  
  13.         int tmp = lx[x]+ly[y]-G[x][y];  
  14.         if(tmp == 0){  
  15.             visy[y] = true;  
  16.             if(linker[y] == -1 || dfs(linker[y])){  
  17.                 linker[y] = x;  
  18.                 return true;  
  19.             }  
  20.         }  
  21.         else if(slack[y] > tmp) slack[y] = tmp;  
  22.     }  
  23.     return false;  
  24. }  
  25.   
  26. int KM(){  
  27.     memset(linker, -1, sizeof(linker));  
  28.     memset(ly, 0, sizeof(ly));  
  29.     for(int i = 0; i < n; ++i){  
  30.         lx[i] = -INF;  
  31.         for(int j = 0; j < n; ++j)  
  32.             if(G[i][j] > lx[i]) lx[i] = G[i][j];  
  33.     }  
  34.     for(int x = 0; x < n; ++x){  
  35.         for(int i = 0; i < n; ++i) slack[i] = INF;  
  36.         while(true){  
  37.             memset(visx, 0, sizeof(visx));  
  38.             memset(visy, 0, sizeof(visy));  
  39.             if(dfs(x)) break;  
  40.             int delta = INF;  
  41.             for(int i = 0; i < n; ++i)  
  42.                 if(!visy[i] && delta > slack[i]) delta = slack[i];  
  43.             for(int i = 0; i < n; ++i)  
  44.                 if(visx[i]) lx[i]-=delta;  
  45.             for(int i = 0; i < n; ++i)  
  46.                 if(visy[i]) ly[i]+=delta;  
  47.                 else slack[i] -= delta;  
  48.         }  
  49.     }  
  50.     int res = 0;  
  51.     for(int i = 0; i < n; ++i){  
  52.         if(linker[i] != -1) res += G[linker[i]][i];  
  53.     }  
  54.     return res;  
  55. }  


有向图的强连通分量

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  *Tarjan算法 
  3.  *复杂度O(N+M); 
  4.  */  
  5. const int maxn = 10005; // 点数;  
  6. const int maxm = 100005; // 边数  
  7. int head[maxn], tot;  
  8. int low[maxn], dfn[maxn], Stack[maxn], Belong[maxn]; // Belong数组的数值为1~scc;  
  9. int Index, top, scc; // scc表示强连通分量的个数;  
  10. bool Instack[maxn];  
  11. int num[maxn]; // 各个强连通分量包含点的个数;  
  12.   
  13.   
  14. struct Edge{  
  15.     int to, next;  
  16. }edge[maxm];  
  17.   
  18.   
  19. void addedge(int u, int v){  
  20.     edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++;  
  21. }  
  22. void Tarjan(int u){  
  23.     int v;  
  24.     low[u] = dfn[u] = ++Index;  
  25.     Stack[top++] = u;  
  26.     Instack[u] = true;  
  27.     for(int i = head[u]; i != -1; i = edge[i].next){  
  28.         v = edge[i].to;  
  29.         if(!dfn[v]){  
  30.             Tarjan(v);  
  31.             low[u] = min(low[u], low[v]);  
  32.         } else if(Instack[v]) low[u] = min(low[u], dfn[v]);  
  33.     }  
  34.     if(low[u] == dfn[u]){  
  35.         scc++;  
  36.         do{  
  37.             v = Stack[--top];  
  38.             Instack[v] = false;  
  39.             Belong[v] = scc;  
  40.             num[scc]++;  
  41.         } while(v!=u);  
  42.     }  
  43. }  
  44.   
  45.   
  46. void solve(){  
  47.     memset(dfn, 0, sizeof(dfn));  
  48.     memset(Instack, falsesizeof(Instack));  
  49.     memset(num, 0, sizeof(num));  
  50.     Index = scc = top = 0;  
  51.     for(int i = 0; i < n; ++i){  
  52.         if(!dfn[i])  
  53.             Tarjan(i);  
  54.     }  
  55. }  
  56. void init(){  
  57.     memset(head, -1, sizeof(head));  
  58.     tot = 0;  
  59. }  
  60.   


最短路径算法:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // dijkstra 模板; O(N^2)  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int g[maxn][maxn], dist[maxn], n;   //dist保存单源最短路径;   
  2. bool vis[maxn];    
  3. void dijkstra(int u){  // u表示起始位置;  
  4.     for(int i = 0; i < n; ++i) dist[i] = INF, vis[i] = false;  
  5.     dist[u] = 0;  
  6.     for(int j = 0; j < n-1; ++j){  
  7.         int k = -1;  
  8.         int Min = INF;  
  9.         for(int i = 0; i < n; ++i)  
  10.             if(!vis[i] && dist[i] < Min){ Min = dist[i]; k = i; }  
  11.         if(k == -1) break;  
  12.         vis[k] = true;  
  13.         for(int i = 0; i < n; ++i)  
  14.             if(!vis[i] && dist[k]+g[k][i]<dist[i])  
  15.                 dist[i] = dist[k]+g[k][i];  
  16.     }  
  17. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 使用优先队列优化Dijkstra算法; 
  3.  * 复杂度 O(ElogE) 
  4.  */  
  5. int dist[maxn], n;  
  6. struct qnode{  
  7.     int v, d;  
  8.     qnode(int _v, int _d):v(_v), d(_d){}  
  9.     bool operator < (const qnode &rhs)const{  
  10.         return d > rhs.d;  
  11.     }  
  12. };  
  13. struct Edge{  
  14.     int v, w;  
  15.     Edge(int _v, int _w):v(_v), w(_w){}  
  16. };  
  17. vector<Edge> G[maxn];  
  18.   
  19. void dijkstra(int u){  
  20.     priority_queue<qnode> pq;  
  21.     for(int i = 0; i < n; ++i) dist[i] = INF;  
  22.     dist[u] = 0;  
  23.     pq.push(qnode(u, 0));  
  24.     while(!pq.empty()){  
  25.         qnode x = pq.top(); pq.pop();  
  26.         int u = x.v;  
  27.         if(x.d != dist[u]) continue;  
  28.         for(unsigned i = 0; i < G[u].size(); ++i){  
  29.             int v = G[u][i].v;  
  30.             if(dist[u]+G[u][i].w < dist[v]){  
  31.                 dist[v] = dist[u]+G[u][i].w;  
  32.                 pq.push(qnode(v, dist[v]));  
  33.             }  
  34.         }  
  35.     }  
  36. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 单源最短路径Bellman-Ford 算法 
  2.  * 时间复杂度O(VE); 
  3.  * 可处理负环; 
  4.  */  
  5.  int dist[maxn], n;  
  6.  struct Edge{  
  7.     int u, v, w;  
  8.     Edge(int _u, int _v, int _w): u(_u), v(_v), w(_w){}  
  9.  };  
  10.  vector<Edge> E;  
  11.   
  12. bool bellman_Ford(int s){  
  13.     for(int i = 0; i < n; ++i) dist[i] = INF;  
  14.     dist[s] = 0;  
  15.     for(int k = 0; k < n-1; ++k){ // 最多经过n-1个节点(迭代n-1次);  
  16.         bool flag = false;  
  17.         for(int i = 0; i < E.size(); ++i){  //检查每一条边;  
  18.             int u = E[i].u, v = E[i].v;  
  19.             if(dist[u] < INF && dist[v] > dist[u]+E[i].w){  
  20.                 dist[v] = dist[u]+E[i].w;  
  21.                 flag = true;  
  22.             }  
  23.         }  
  24.         if(!flag) return true// 没有负环回路;  
  25.     }  
  26.     for(int i = 0; i < E.size(); ++i){  
  27.         int u = E[i].u, v = E[i].v;  
  28.         if(dist[v] > dist[u]+E[i].w) return false;  
  29.     }  
  30.     return true;  
  31.  }  



[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 单源最短路径SPFA算法 
  2.  * 时间复杂度O(KE) ; 复杂度不稳定; 
  3.  * 可处理负环; 
  4.  */  
  5. int n, dist[maxn], cnt[maxn]; //判断一个节点在队列中出现的次数;  
  6. bool inq[maxn]; // 判断是否在队列中;  
  7.   
  8. struct Edge{  
  9.     int v, w;  
  10.     Edge(int _v, int _w): v(_v), w(_w){};  
  11. };  
  12. vector<Edge> G[maxn];  
  13.   
  14. bool bellman_ford(int s){  
  15.     queue<int> q;  
  16.     memset(inq, 0, sizeof(inq));  
  17.     memset(cnt, 0, sizeof(cnt));  
  18.     for(int i = 0; i < n; ++i) dist[i] = INF;  
  19.     dist[s] = 0;  
  20.     inq[s] = true;  
  21.     q.push(s);  
  22.   
  23.     while(!q.empty()){  
  24.         int u = q.front(); q.pop();  
  25.         inq[u] = false;  
  26.         for(int i = 0; i < G[u].size(); ++i){  
  27.             int v = G[u][i].v;  
  28.             if(dist[u] < INF && dist[v] > dist[u]+G[u][i].w){  
  29.                 dist[v] = dist[u]+G[u][i].w;  
  30.                 if(!inq[v]) { q.push(v); inq[v] = trueif(++cnt[v] > n) return false; }  
  31.             }  
  32.         }  
  33.     }  
  34.     return true;  
  35. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /*  
  2.      *Floyd 算法; 
  3.      *调用之前简单的初始化 d[i][i] = 0, 其他d值为"INF"; 
  4.      *复杂度O(N^3) 
  5.      */  
  6.     for(int k = 0; k < n; ++k)  
  7.         for(int i = 0; i < n; ++i)  
  8.             for(int j = 0; j < n; ++j)  
  9.                 d[i][j] = min(d[i][j], d[i][k]+d[k][j]);  



[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 最小生成树 prim 模板; 
  2.  * 时间复杂度 O(V*V); 适合稠密图; 
  3.  */  
  4. int dist[maxn], G[maxn][maxn], n, m;  
  5. bool  vis[maxn];  
  6.   
  7. int prim(){  
  8.     int res = 0;  
  9.     memset(vis, 0, sizeof(vis));  
  10.     vis[0] = true;  
  11.     for(int i = 1; i < n; ++i) dist[i] = G[0][i];  
  12.     for(int k = 0; k < n-1; ++k){  // 还需要添加n-1个节点;  
  13.         int _min = INF, p = -1;  
  14.         for(int i = 0; i < n; ++i)  
  15.             if(!vis[i] && dist[i] < _min) _min = dist[i], p = i;  
  16.         if(_min == INF) return -1; //原图不连通;  
  17.         res += _min;  
  18.         vis[p] = true;  
  19.         for(int i = 0; i < n; ++i)  
  20.             if(!vis[i] && G[p][i] < dist[i]) dist[i] = G[p][i];  
  21.     }  
  22.     return res;  
  23. }  



[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 次小生成树; 
  3.  * 求最小生成树时, Max[i][j]表示MST中i到j最大边权; 
  4.  * 求完后,直接枚举所有不在MST中的边, 替换掉最大边的权, 跟新答案; 
  5.  */  
  6. int n, m, G[maxn][maxn], dist[maxn], Max[maxn][maxn], pre[maxn];  
  7. bool vis[maxn], used[maxn][maxn];  
  8.   
  9. int prim(){  
  10.     int res = 0;  
  11.     memset(vis, 0, sizeof(vis));  
  12.     memset(Max, 0, sizeof(Max));  
  13.     memset(used, 0, sizeof(used));  
  14.     vis[0] = 1;  
  15.     pre[0] = -1;  
  16.     for(int i = 1; i < n; ++i) { dist[i] = G[0][i]; pre[i] = 0; }  
  17.     for(int k = 0; k < n-1; ++k){  
  18.         int _min = INF, p = -1;  
  19.         for(int i = 0; i < n; ++i)  
  20.             if(!vis[i] && dist[i] < _min) _min = dist[i], p = i;  
  21.         if(_min == INF) return -1;  
  22.         res += _min;  
  23.         vis[p] = true;  
  24.         used[p][pre[p]] = used[pre[p]][p] = true;  
  25.         for(int i = 0; i < n; ++i){  
  26.             if(vis[i] && i != p) Max[i][p] = Max[p][i] = max(Max[i][pre[p]], dist[p]);  
  27.             if(!vis[i] && G[p][i] < dist[i]) { dist[i] = G[p][i]; pre[i] = p; }  
  28.         }  
  29.     }  
  30.     return res;  
  31. }  

猜你喜欢

转载自blog.csdn.net/wangzhuo0978/article/details/70244336
今日推荐