模板-图论

图论算法相关模版, 可能有错误, 省选前持续更正中

重要的不是模版内容, 而是提供算法的实现思路.

struct SPFA {
	int n, m, s, t;
	int d[maxn];
	bool ban[maxn], inq[maxn];
	vector
   
   
    
     edges;
	vector
    
    
     
      G[maxn];

	void init(int n, int s, int t) {
		this->n = n;
		this->s = s;
		this->t = t;
	}

	void AddEdge(int from, int to, int dist) {
		edges.push_back((Edge) {from, to, dist});
		edges.push_back((Edge) {to, from, dist});
		m = edges.size();
		G[from].push_back(m-2);
		G[to].push_back(m-1);
	}

	int spfa(int x, int y) {
		queue
     
     
      
       Q;
		memset(d, 0x3f, sizeof(d));
		Q.push(s);
		inq[s] = 1;
		d[s] = 0;

		while(!Q.empty()) {
			int u = Q.front();
			Q.pop();
			inq[u] = 0;
			for(int i = 0; i < G[u].size(); i++) {
				Edge& e = edges[G[u][i]];
				if(!ban[e.to] && d[e.to] > d[u] + e.dist) {
					d[e.to] = d[u] + e.dist;
					if(!inq[e.to]) Q.push(e.to), inq[e.to] = 1;
				}
			}
		}
		return d[t];
	}
};


struct Dijkstra {
	int n, m;
	vector
      
      
       
        edges;
	vector
       
       
         G[maxn]; bool done[maxn]; int d[maxn], p[maxn]; void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int dist) { edges.push_back((Edge) {from, to, dist}); m = edges.size(); G[from].push_back(m-1); } void dijkstra(int s) { priority_queue 
        
          Q; // HeapNode 自定义结构体 // 存储二元组 {结点距s距离(dist), 结点编号(u)} // 并根据 dist 进行排序 for(int i = 0; i < n; i++) d[i] = INF; d[s] = 0; memset(done, 0, sizeof(done)); // 永久编号 Q.push((HeapNode){0, s}); while(!Q.empty()) { HeapNode x = Q.top(); Q.pop(); int u = x.u; if(done[u]) continue; done[u] = true; for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(d[e.to] > d[u] + e.dist) { d[e.to] = d[u] + e.dist; p[e.to] = G[u][i]; Q.push((HeapNode){d[e.to], e.to}); } } } } void GetShortestPaths(int s, int* dist, vector 
         
           * paths) { dijkstra(s); for(int i = 0; i < n; i++) { dist[i] = d[i]; paths[i].clear(); int t = i; paths[i].push_back(t); while(t != s) { paths[i].push_back(edges[p[t]].from); t = edges[p[t]].from; } reverse(paths[i].begin(), paths[i].end()); } } }; struct MST { int n, m; void AddEdge(int from, int to, int dist) { edges.push_back((Edge){from, to, dist}); m = edges.size(); G[from].push_back(m-1); } int mst() { int ans = 0; sort(edges.begin(), edges.end()); for(int i = 0; i < m; i++) { Edge& e = edges[i]; int x = ufs.find(e.from), y = ufs.find(e.to); if(x != y) ufs.unin(x, y), ans += e.dist; } return ans; } }; struct TwoSAT { int n; vector 
          
            G[maxn*2]; bool mark[maxn*2]; int S[maxn*2], c; bool dfs(int x) { if (mark[x^1]) return false; if (mark[x]) return true; mark[x] = true; S[c++] = x; for (int i = 0; i < G[x].size(); i++) if (!dfs(G[x][i])) return false; return true; } void init(int n) { this->n = n; for (int i = 0; i < n*2; i++) G[i].clear(); memset(mark, 0, sizeof(mark)); } // x = xval or y = yval void add_clause(int x, int xval, int y, int yval) { x = x * 2 + xval; y = y * 2 + yval; G[x^1].push_back(y); G[y^1].push_back(x); } bool solve() { for(int i = 0; i < n*2; i += 2) if(!mark[i] && !mark[i+1]) { c = 0; if(!dfs(i)) { while(c > 0) mark[S[--c]] = false; if(!dfs(i+1)) return false; } } return true; } }; // 判定二分图 bool bipartite(int u, int b) { for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(bccno[v] != b) continue; if(color[v] == color[u]) return false; if(!color[v]) { color[v] = 3 - color[u]; if(!bipartite(v, b)) return false; } } return true; } // 双联通分量算法 stack 
           
             S; int dfs(int u, int fa) { int lowu = pre[u] = ++dfs_clock; int child = 0; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; Edge e = (Edge){u, v}; if(!pre[v]) { // 没有访问过v S.push(e); child++; int lowv = dfs(v, u); lowu = min(lowu, lowv); // 用后代的low函数更新自己 if(lowv >= pre[u]) { iscut[u] = true; bcc_cnt++; bcc[bcc_cnt].clear(); for(;;) { Edge x = S.top(); S.pop(); if(bccno[x.u] != bcc_cnt) { bcc[bcc_cnt].push_back(x.u); bccno[x.u] = bcc_cnt; } if(bccno[x.v] != bcc_cnt) { bcc[bcc_cnt].push_back(x.v); bccno[x.v] = bcc_cnt; } if(x.u == u && x.v == v) break; } } } else if(pre[v] < pre[u] && v != fa) { S.push(e); lowu = min(lowu, pre[v]); // 用反向边更新自己 } } if(fa < 0 && child == 1) iscut[u] = 0; return lowu; } void find_bcc(int n) { memset(pre, 0, sizeof(pre)); memset(iscut, 0, sizeof(iscut)); memset(bccno, 0, sizeof(bccno)); dfs_clock = bcc_cnt = 0; for(int i = 0; i < n; i++) if(!pre[i]) dfs(i, -1); } struct Tarjan { vector 
            
              G[maxn]; int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt; stack 
             
               S; void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u], lowlink[v]); } else if(!sccno[v]) { // notice lowlink[u] = min(lowlink[u], pre[v]); } } if(lowlink[u] == pre[u]) { scc_cnt++; for(;;) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if(x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(sccno, 0, sizeof(sccno)); memset(pre, 0, sizeof(pre)); for(int i = 0; i < n; i++) if(!pre[i]) dfs(i); } }; struct TOPO { int n, m, d[maxn], d0[maxn]; vector 
              
                edges; vector 
               
                 G[maxn]; void init(int n) { this->n = n; memset(d0, 0, sizeof(d0)); edges.clear(); for(int u = 1; u <= n; u++) G[u].clear(); } void AddEdge(int from, int to, int dist) { edges.push_back((Edge){to, dist}); m = edges.size(); G[from].push_back(m-1); d0[to]++; } int toposort() { queue 
                
                  Q; memset(d, 0, sizeof(d)); for(int u = 1; u <= n; u++) if(G[u].size() != 0 && !d0[u]) Q.push(u); int max_d = 0; while(!Q.empty()) { int u = Q.front(); Q.pop(); max_d = max(max_d, d[u]); // 最长路 for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; d[e.to] = max(d[e.to], d[u] + e.dist); if(--d0[e.to] == 0) Q.push(e.to); } } return max_d; } }; // 二分图最大基数匹配 struct Match { vector 
                 
                   G[maxn]; bool vis[maxn]; int dfs(int u) { for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(!vis[v]) { vis[v] = 1; if(y[v] == -1 || dfs(y[v])) { x[u] = v; y[v] = u; return 1; } } } return 0; } int match() { int ans = 0; memset(y, -1, sizeof(y)); for(int i = 1; i <= m; i++) { memset(vis, 0, sizeof(vis)); ans += dfs(i); } return ans; } }; // 最小环 struct Floyed { int n; int G[maxn][maxn], f[maxn][maxn]; int floyed() { int ans = INF; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) f[i][j] = G[i][j]; // 可以直接枚举 k, i, j, 在计算 f 的值的同时更新 ans. // 即 : /* for(int k = 0; k < n; k++) { for(int i = 0; i < n; i++) if(G[i][k] != INF) for(int j = 0; j < n; j++) if(i != k && i != j && k != j) { ans = min(ans, f[i][j] + G[i][k] + G[k][j]); f[i][j] = min(f[i][j], f[i][k] + f[k][j]); } } */ // 也是种可行的做法 // 下面是优化过的做法 : for(int k = 0; k < n; k++) { // 枚举编号最大点 for(int i = 0; i < k; i++) if(G[i][k] != INF) for(int j = i+1; j < k; j++) // 如果为有向边, j 从 0 开始枚举 ans = min(ans, f[i][j] + G[i][k] + G[k][j]); // 此时 f[i][j] 一定没有用 k 更新过 // 所以 f[i][j] 表示的路径不过 k // 更新最短路 for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) f[i][j] = min(f[i][j], f[i][k] + f[k][j]); // 用 f[][] 而不是 G[][] 在更新答案 } return ans == INF ? -1 : ans; } }; // 无向图欧拉路径 struct Euler { int n; bool G[maxn][maxn], vis[maxn][maxn]; vector 
                  
                    ans; void euler(int u) { for(int v = 0; v < n; v++) if(G[u][v] && !vis[u][v]) { vis[u][v] = vis[v][u] = 1; euler(v); ans.push_back((Edge){u, v}); } } int judge(int start) { euler(start); if(ans.size() == n && ans[0].to == ans[ans.size()-1].from) return 1; return 0; } }; struct LCA { int n; int fa[maxn]; // 父亲数组 int cost[maxn]; // 和父亲的费用 int L[maxn]; // 层次(根节点层次为0) int anc[maxn][logmaxn]; // anc[p][i]是结点p的第2^i级父亲。anc[i][0] = fa[i] int maxcost[maxn][logmaxn]; // maxcost[p][i]是i和anc[p][i]的路径上的最大费用 // 预处理,根据fa和cost数组求出anc和maxcost数组 void preprocess() { for(int i = 0; i < n; i++) { anc[i][0] = fa[i]; maxcost[i][0] = cost[i]; for(int j = 1; (1< 
                   
                     < n; j++) anc[i][j] = -1; } for(int j = 1; (1< 
                    
                      < n; j++) for(int i = 0; i < n; i++) if(anc[i][j-1] != -1) { int a = anc[i][j-1]; anc[i][j] = anc[a][j-1]; maxcost[i][j] = max(maxcost[i][j-1], maxcost[a][j-1]); } } // 求p到q的路径上的最大权 int query(int p, int q) { int tmp, log, i; if(L[p] < L[q]) swap(p, q); for(log = 1; (1 << log) <= L[p]; log++); log--; int ans = -INF; for(int i = log; i >= 0; i--) if (L[p] - (1 << i) >= L[q]) { ans = max(ans, maxcost[p][i]); p = anc[p][i]; } if (p == q) return ans; // LCA为p for(int i = log; i >= 0; i--) if(anc[p][i] != -1 && anc[p][i] != anc[q][i]) { ans = max(ans, maxcost[p][i]); p = anc[p][i]; ans = max(ans, maxcost[q][i]); q = anc[q][i]; } ans = max(ans, cost[p]); ans = max(ans, cost[q]); return ans; // LCA为fa[p](它也等于fa[q]) } }; // 固定根的最小树型图,邻接矩阵写法 struct MDST { int n; int w[maxn][maxn]; // 边权 int vis[maxn]; // 访问标记,仅用来判断无解 int ans; // 计算答案 int removed[maxn]; // 每个点是否被删除 int cid[maxn]; // 所在圈编号 int pre[maxn]; // 最小入边的起点 int iw[maxn]; // 最小入边的权值 int max_cid; // 最大圈编号 void init(int n) { this->n = n; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) w[i][j] = INF; } void AddEdge(int u, int v, int cost) { w[u][v] = min(w[u][v], cost); // 重边取权最小的 } // 从s出发能到达多少个结点 int dfs(int s) { vis[s] = 1; int ans = 1; for(int i = 0; i < n; i++) if(!vis[i] && w[s][i] < INF) ans += dfs(i); return ans; } // 从u出发沿着pre指针找圈 bool cycle(int u) { max_cid++; int v = u; while(cid[v] != max_cid) { cid[v] = max_cid; v = pre[v]; } return v == u; } // 计算u的最小入弧,入弧起点不得在圈c中 void update(int u) { iw[u] = INF; for(int i = 0; i < n; i++) if(!removed[i] && w[i][u] < iw[u]) { iw[u] = w[i][u]; pre[u] = i; } } // 根结点为s,如果失败则返回false bool solve(int s) { memset(vis, 0, sizeof(vis)); if(dfs(s) != n) return false; memset(removed, 0, sizeof(removed)); memset(cid, 0, sizeof(cid)); for(int u = 0; u < n; u++) update(u); pre[s] = s; iw[s] = 0; // 根结点特殊处理 ans = max_cid = 0; for(;;) { bool have_cycle = false; for(int u = 0; u < n; u++) if(u != s && !removed[u] && cycle(u)) { have_cycle = true; // 以下代码缩圈,圈上除了u之外的结点均删除 int v = u; do { if(v != u) removed[v] = 1; ans += iw[v]; // 对于圈外点i,把边i->v改成i->u(并调整权值);v->i改为u->i // 注意圈上可能还有一个v'使得i->v'或者v'->i存在,因此只保留权值最小的i->u和u->i for(int i = 0; i < n; i++) if(cid[i] != cid[u] && !removed[i]) { if(w[i][v] < INF) w[i][u] = min(w[i][u], w[i][v]-iw[v]); w[u][i] = min(w[u][i], w[v][i]); if(pre[i] == v) pre[i] = u; } v = pre[v]; } while(v != u); update(u); break; } if(!have_cycle) break; } for(int i = 0; i < n; i++) if(!removed[i]) ans += iw[i]; return true; } }; // 以下为网络流 struct Dinic { int n, m, s, t; int first[maxm], next[maxm]; Edge edges[maxm]; bool vis[maxn]; int d[maxn], cur[maxn]; void init(int n, int s, int t) { this->n = n; this->m = 1; this->s = s; this->t = t; } void AddEdge(int u, int v, int cap) { edges[++m] = (Edge){u, v, cap, 0}; next[m] = first[u]; first[u] = m; edges[++m] = (Edge){v, u, 0, 0}; next[m] = first[v]; first[v] = m; } bool BFS() { memset(vis, 0, sizeof(vis)); queue 
                     
                       Q; Q.push(s); vis[s] = 1; d[s] = 0; while(!Q.empty()) { int u = Q.front(); Q.pop(); for(int i = first[u]; i; i = next[i]) { Edge& e = edges[i]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = 1; d[e.to] = d[u] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int u, int a) { if(u == t || a == 0) return a; int flow = 0, f; for(int& i = cur[u]; i; i = next[i]) { Edge& e = edges[i]; if(d[u] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { edges[i].flow += f; edges[i^1].flow -= f; flow += f; a -= f; if(a == 0) break; } } return flow; } int Maxflow() { int flow = 0; while(BFS()) { for(int u = 0; u < n; u++) cur[u] = first[u]; flow += DFS(s, INF); } return flow; } }; struct MCMF { int n, m, s, t; Edge edges[maxm]; int first[maxn], next[maxn]; int inq[maxn], d[maxn], p[maxn], a[maxn]; void init(int n, int s, int t) { this->n = n; this->m = 1; this->s = s; this->t = t; } void AddEdge(int u, int v, int cap, int cost) { edges[++m] = (Edge){u, v, cap, 0, cost}; next[m] = first[u]; first[u] = m; edges[++m] = (Edge){v, u, 0, 0, -cost}; next[m] = first[v]; first[v] = m; } bool BellmanFord(int& cost) { for(int i = 0; i < n; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue 
                      
                        Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i = first[u]; i; i = next[i]) { Edge& e = edges[i]; if(e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = i; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return false; cost += d[t] * a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } int Mincost() { int cost = 0; while(BellmanFord(cost)); return cost; } }; 
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值