图论模板整理
来源:互联网 发布:linux 文件所有者 编辑:程序博客网 时间:2024/05/14 23:36
模板大部分来自LRJ
连通性
割点
<pre name="code" class="cpp">//带重边处理int tarjan(int u, int fa){ bool f = false; int lowu = dfn[u] = ++dfs_c; REP(i, G[u].size()) { Edge& e = edges[G[u][i]]; if (e.to == fa && !f)///////第一次为访问父节点不算, 其他则为重边 { f = 1; continue; } if (!dfn[e.to]) { int lowv = tarjan(e.to, u); lowu = min(lowu, lowv); if (lowv > dfn[u]) { edges[G[u][i]].flag = true; edges[G[u][i] ^ 1].flag = true; } } else lowu = min(lowu, dfn[e.to]); } low[u] = lowu; return lowu;}vector<int> G[MAXN];int pre[MAXN], low[MAXN], iscut[MAXN];///pre[u]表示dfs中u的次序编号///low[u]表示u及u的子树中能追溯到的最早的节点的次序编号值(即pre值)///iscut[u]表示u是否为割点int dfs_clock = 0;///调用前需将pre数组赋0,首次调用fa为-1int dfs(int u, int fa)///fa表示u的父节点{ int lowu = pre[u] = ++dfs_clock; int child = 0; ///表示子节点个数 for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) ///若没被访问 { int lowv = dfs(v, u); lowu = min(lowv, lowu); ///用子节点的low值更新lowu if (lowv >= pre[u] && fa >= 0) { iscut[u] = 1; } }///若被访问,则用反向变(返祖边)更新当前u的low值 else if (v != fa && pre[v] < pre[u])///切记v != fa { lowu = min(lowu, pre[v]); } } if (fa < 0 && child > 1) ///根节点为割点当且仅当子节点数大于一个 iscut[u] = 1; low[u] = lowu; return lowu;}求删割点后剩余联通分量数时,只用将iscut[u] = 1改为iscut[u]++,则非根节点剩余分量数为iscut[u]+1,根节点为iscut[u](若u为割点,记iscut[u]为u的子节点数,则去掉u后,图被分成iscut[u]+1个部分(每个子节点的部分和u的祖先的部分),若u为dfs树的根,则分成iscut[u]个部分(根节点没有祖先))
点双连通分量
<pre name="code" class="cpp">//当u为割点时,把边从栈顶依次取出,直到遇到边(u,v),取出的边与其关联的点,组成点双连通分支。//割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支//基本算法与求割点一致int dfn[MAXN], iscut[MAXN], bccno[MAXN];int dfs_clock, bcc_cnt;vector<int> G[MAXN], bcc[MAXN];struct Edge{ int u, v;};stack<Edge> S;int dfs(int u, int fa){ int lowu = dfn[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 (!dfn[v]) { S.push(e); child++; int lowv = dfs(v, u); lowu = min(lowu, lowv); if (lowv >= dfn[u]) { iscut[u] = 1; bcc_cnt++; bcc[bcc_cnt].clear(); while (1) { 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)///最后才判断跳出,因为(u,v)这条变也要加入 break; } } } else if (v != fa && dfn[v] < dfn[u]) { S.push(e); lowu = min(lowu, dfn[v]); } } if (fa < 0 && child == 1) iscut[u] = 0; return lowu;}void find_bcc(int n){ memset(dfn, 0, sizeof(dfn)); memset(iscut, 0, sizeof(iscut)); memset(bccno, 0, sizeof(bccno)); dfs_clock = bcc_cnt = 0; for (int i = 1; i <= n; i++) if (!dfn[i]) dfs(i, -1);}
边双连通分量
struct Edge{ int from, to; bool flag;};vector<Edge> edges;VI G[maxn];int dfn[maxn], bccno[maxn], out[maxn];int dfs_c, bcc_cnt, n, m;bool vis[maxn];inline void add(int x, int y){ edges.PB((Edge){x, y, false}); edges.PB((Edge){y, x, false}); int sz = edges.size(); G[x].PB(sz - 2); G[y].PB(sz - 1);}int tarjan(int u, int fa){ int lowu = dfn[u] = ++dfs_c; REP(i, SZ(G[u])) { Edge& e = edges[G[u][i]]; if (!dfn[e.to]) { int lowv = tarjan(e.to, u); lowu = min(lowu, lowv); if (lowv > dfn[u]) { e.flag = true; edges[G[u][i] ^ 1].flag = 1; } } else if (dfn[e.to] < dfn[u] && e.to != fa) lowu = min(lowu, dfn[e.to]); } return lowu;}void find_bcc(){ CLR(dfn, 0), CLR(bccno, 0); CLR(out, 0); dfs_c = bcc_cnt = 0; FE(i, 1, n) if (!dfn[i]) tarjan(i, -1);}void init(){ FE(i, 0, n + 1) G[i].clear(); edges.clear(); CLR(vis, 0);}void dfs(int u, int cnt){ vis[u] = 1, bccno[u] = cnt; REP(i, SZ(G[u])) { Edge& e = edges[G[u][i]]; if (e.flag) { //printf("Bridge: %d %d\n", e.from, e.to); continue; } if (!vis[e.to]) dfs(e.to, cnt); }}void solve(){ FE(i, 1, n) if (!vis[i]) { bcc_cnt++; dfs(i, bcc_cnt); } REP(i, SZ(edges)) { int u = edges[i].from, v = edges[i].to; if (bccno[u] != bccno[v]) out[bccno[u]]++; } int leaf = 0; FE(i, 1, bcc_cnt) if (out[i] == 1) leaf++; //cout << bcc_cnt << " " << leaf << endl; int ans = (leaf + 1) / 2; if (bcc_cnt == 1) ans = 0; WI(ans);}int main(){ int x, y; while (~RII(n, m)) { init(); REP(i, m) { RII(x, y); add(x, y); } find_bcc(); solve(); }}重边处理//对于有重边的图,求边双连通分量struct EDGE{int u, v;int next;};int first[MAXN], rear;EDGE edge[MAXE];void init(){memset(first, -1, sizeof(first));rear = 0;}void insert(int tu, int tv){edge[rear].u = tu;edge[rear].v = tv;edge[rear].next = first[tu];first[tu] = rear++;edge[rear].u = tv;edge[rear].v = tu;edge[rear].next = first[tv];first[tv] = rear++;}int pre[MAXN], low[MAXN];bool vis_e[MAXE];bool is_cut_e[MAXE];int dfs_clock;int dfs(int cur, int fa){pre[cur] = low[cur] = ++dfs_clock;int child(0);for(int i = first[cur]; i != -1; i = edge[i].next){int tv = edge[i].v;if(!pre[tv]){++child;vis_e[i] = true;vie_e[i^1] = true;st.push(i);int lowv = dfs(tv, cur);low[cur] = min(low[cur], lowv);}elseif(pre[tv] < pre[cur] && !vis_e[i]){vis_e[i] = true;vis_e[i^1] = true;low[cur] = min(low[cur], pre[edge[i].v]);}}return low[cur];}void find_e_bccn(int n){dfs_clock = 0;memset(pre, 0, sizeof(pre));memset(low, 0, sizeof(low));memset(vis_e, 0, sizeof(vis_e));memset(is_cut_e, 0, sizeof(is_cut_e));for(int i = 1; i <= n; ++i)if(!pre[i])dfs(i);for(int i = 0; i < rear; ++i)if(low[i].v < pre[edge[i].u]){is_cut_e[i] = true;is_cut_e[i^1] = true;}}//接着在不经过桥的情况下dfs求出所有双强连通分量即可
强连通分量
vector<int> G[MAXN];int dfn[MAXN], low[MAXN], sccno[MAXN], dfs_clock, scc_cnt;stack<int> S;void dfs(int u){ dfn[u] = low[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[u]) { dfs(v); low[u] = min(low[u], low[v]); } else if (!sccno[v]) ///只能通过当前SCC中的点更新 low[u] = min(low[u], low[v]); } if (low[u] == dfn[u]) ///当u是此连通分量中最先被遍历的点 { scc_cnt++; while (1) { 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(dfn, 0, sizeof(dfn)); for (int i = 0; i < n; i++) if (!dfn[i]) dfs(i);}//求至少需要选择多少个点才能是全图都能被访问到//至少加多少条边才能使全图连通//解:缩点,行成一个DAG图,则A为DAG中入度为0的点(块)的个数// B为入度或出度为0的点的个数的最大值
最短路
dijkstra算法(优先队列优化)
struct Edge{ int from, to, dist;};struct Node{ int d, u; bool operator < (const Node& rhs) const { return d > rhs.d; }};struct Dijkstra{ int n, m; vector<Edge> edges; vector<int> G[MAXN]; bool done[MAXN]; int d[MAXN]; int 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<Node> Q; for (int i = 0; i < n; i++) d[i] = INF; d[s] = 0; memset(done, 0, sizeof(done)); Q.push((Node){0, s}); while (!Q.empty()) { Node x = Q.top(); Q.pop(); int u = x.u; if (done[u]) continue; done[u] = 1; 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((Node){d[e.to], e.to}); } } } }};
spfa算法判负环
struct Edge{ int from, to, dist;};struct BellmanFord{ int n, m; vector<Edge> edges; vector<int> G[MAXN]; bool inq[MAXN]; int d[MAXN]; int p[MAXN]; int cnt[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); } bool negativeCycle() { queue<int> Q; memset(inq, 0, sizeof(inq)); memset(cnt, 0, sizeof(cnt)); for (int i = 0; i < n; i++)///若使用spfa算法,则修改为源点入队,d[]数组赋值INF,d[s]=0 { d[i] = 0; inq[i] = true; Q.push(i); } 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 (d[e.to] > d[u] + e.dist) { d[e.to] = d[u] + e.dist; p[e.to] = G[u][i]; if (!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; if (++cnt[e.to] > n) return true; } } } } return false; }};//DFS版本判负环struct BellmanFord{int n, m;vector<Edge> edges;VI G[maxn];bool inq[maxn];double d[maxn];int cnt[maxn];void init(int n){ this-> n = n; REP(i, n + 1) G[i].clear(); edges.clear();}void add(int from, int to, double dist){ Edge e1 = Edge(from, to, dist); edges.PB(e1); m = edges.size(); G[from].PB(m - 1);}bool dfs(int u){ inq[u] = 1; REP(i, SZ(G[u])) { Edge &e = edges[G[u][i]]; if (d[e.to] > d[u] * e.dist) { d[e.to] = d[u] * e.dist; if (inq[e.to] || dfs(e.to)) return 1; } } inq[u] = 0; return 0;}bool spfa(int s){ queue<int> Q; CLR(inq, false), CLR(cnt, 0); REP(i, n) d[i] = INF; d[s] = 1.0, inq[s] = 1; Q.push(s); REP(i, n) if (dfs(i)) return 1; return 0;}}neg;
K短路模版
const int INF = 0x3f3f3f3f;const int MAX = 1005;int n,m;int start,end,k;struct Edge{ int w; int to; int next;};Edge e[100005];int head[MAX],edgeNum;int dis[MAX]; //dis[i]表示从i点到end的最短距离bool vis[MAX];int cnt[MAX];vector<Edge> opp_Graph[MAX];struct Node{ int f,g; //f = g+dis[v] int v; //当前到达的节点 Node(int a, int b,int c):f(a),g(b),v(c){} bool operator < (const Node& a) const { return a.f < f; }};void addEdge(int from, int to, int w){ e[edgeNum].to = to; e[edgeNum].w = w; e[edgeNum].next = head[from]; head[from] = edgeNum++;}void dijikastra(int start){ int i; memset(vis,0,sizeof(vis)); for(i = 1; i <= n; i++) dis[i] = INF; dis[start] = 0; priority_queue<Node> que; que.push(Node(0,0,start)); Node next(0,0,0); while(!que.empty()) { Node now = que.top(); que.pop(); if(vis[now.v]) //从集合T中选取具有最短距离的节点 continue; vis[now.v] = true; //标记节点已从集合T加入到集合S中 for(i = 0; i < opp_Graph[now.v].size(); i++) //更新从源点到其它节点(集合T中)的最短距离 { Edge edge = opp_Graph[now.v][i]; if(!vis[edge.to] && dis[now.v] + edge.w < dis[edge.to]) //加不加前面的判断无所谓 { dis[edge.to] = dis[now.v] + edge.w; next.f = dis[edge.to]; next.v = edge.to; que.push(next); } } }}int A_Star(){ int i; priority_queue<Node> que; if(dis[start] == INF) return -1; que.push(Node(dis[start],0,start)); Node next(0,0,0); while(!que.empty()) { Node now = que.top(); que.pop(); cnt[now.v]++; if(cnt[end] == k) return now.f; if(cnt[now.v] > k) continue; for(i = head[now.v]; i != -1; i = e[i].next) { next.v = e[i].to; next.g = now.g + e[i].w; next.f = next.g + dis[e[i].to]; que.push(next); } } return -1;}int main(){ int i; int from,to,w; edgeNum = 0; memset(head,-1,sizeof(head)); memset(opp_Graph,0,sizeof(opp_Graph)); memset(cnt,0,sizeof(cnt)); scanf("%d %d",&n,&m); Edge edge; for(i = 1; i <= m; i++) { scanf("%d %d %d",&from,&to,&w); addEdge(from,to,w); edge.to = from; edge.w = w; opp_Graph[to].push_back(edge); } scanf("%d %d %d",&start,&end,&k); if(start == end) k++; dijikastra(end); int result = A_Star(); printf("%d\n",result); return 0;}
匹配问题
3个重要结论:
最小点覆盖数: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数最小路径覆盖=最小路径覆盖=|N|-最大匹配数
用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。
二分图最大独立集=顶点数-二分图最大匹配
在N个点的图G中选出m个点,使这m个点两两之间没有边,求m最大值。
如果图G满足二分图条件,则可以用二分图匹配来做.最大独立集点数 = N - 最大匹配数。
最大匹配(吉大版)
int uN, vN;///切记初始化!!bool g[MAXN][MAXN], visit[MAXN];int xM[MAXN], yM[MAXN];int searchpath(int u){ FE(v, 1, vN) if (g[u][v] && !visit[v]) { visit[v] = 1; if (yM[v] == -1 || searchpath(yM[v])) { xM[u] = v; yM[v] = u; return 1; } } return 0;}int maxmatch(){ int ret = 0; CLR(xM, -1); CLR(yM, -1); FE(u, 1, uN) if (xM[u] == -1) { CLR(visit, 0); ret += searchpath(u); } return ret;}
二分图最大基数匹配,邻接矩阵写法
struct BPM{ int n, m; // 左右顶点个数 int G[maxn][maxn]; // 邻接表 int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在 bool T[maxn]; // T[i]为右边第i个点是否已标记 void init(int n, int m) { this->n = n; this->m = m; memset(G, 0, sizeof(G)); } bool match(int u){ for(int v = 0; v < m; v++) if(G[u][v] && !T[v]) { T[v] = true; if (left[v] == -1 || match(left[v])){ left[v] = u; return true; } } return false; } // 求最大匹配 int solve() { memset(left, -1, sizeof(left)); int ans = 0; for(int u = 0; u < n; u++) { // 从左边结点u开始增广 memset(T, 0, sizeof(T)); if(match(u)) ans++; } return ans; }};
二分图最大基数匹配求覆盖集
struct BPM { int n, m; // 左右顶点个数 vector<int> G[maxn]; // 邻接表 int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在 bool T[maxn]; // T[i]为右边第i个点是否已标记 int right[maxn]; // 求最小覆盖用 bool S[maxn]; // 求最小覆盖用 void init(int n, int m) { this->n = n; this->m = m; for(int i = 0; i < n; i++) G[i].clear(); } void AddEdge(int u, int v) { G[u].push_back(v); } bool match(int u){ S[u] = true; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!T[v]){ T[v] = true; if (left[v] == -1 || match(left[v])){ left[v] = u; right[u] = v; return true; } } } return false; } // 求最大匹配 int solve() { memset(left, -1, sizeof(left)); memset(right, -1, sizeof(right)); int ans = 0; for(int u = 0; u < n; u++) { // 从左边结点u开始增广 memset(S, 0, sizeof(S)); memset(T, 0, sizeof(T)); if(match(u)) ans++; } return ans; } // 求最小覆盖。X和Y为最小覆盖中的点集 int mincover(vector<int>& X, vector<int>& Y) { int ans = solve(); memset(S, 0, sizeof(S)); memset(T, 0, sizeof(T)); for(int u = 0; u < n; u++) if(right[u] == -1) match(u); // 从所有X未盖点出发增广 for(int u = 0; u < n; u++) if(!S[u]) X.push_back(u); // X中的未标记点 for(int v = 0; v < m; v++) if(T[v]) Y.push_back(v); // Y中的已标记点 return ans; }};
二分匹配(邻接表 + 时间戳优化)
struct Edge{int to,next;}edge[MAXM];int head[MAXN],tot;void init(){tot = 0;CLR(head, -1);}void addEdge(int u,int v){edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;}int linker[MAXN];int used[MAXN];//时间戳when优化int uN, when;bool dfs(int u){for(int i = head[u];i != -1;i = edge[i].next){int v = edge[i].to;if(used[v] != when){used[v] = when;if(linker[v] == -1 || dfs(linker[v])){linker[v] = u;return true;}}}return false;}bool hungary(){memset(linker,-1,sizeof(linker));when = 0;for(int u = 0; u < uN;u++){when++;if(!dfs(u))return false;}return true;}
最佳完美匹配(最大权值)
////KM算法int W[maxn][maxn], n;int Lx[maxn], Ly[maxn];int Left[maxn];bool S[maxn], T[maxn];int in[10100], stu[10100];bool match(int i){ S[i] = 1; FE(j, 1, n) if (Lx[i] + Ly[j] == W[i][j] && !T[j]) { T[j] = 1; if (!Left[j] || match(Left[j])) { Left[j] = i; return 1; } } return 0;}void update(){ int a = 1 << 30; FE(i, 1, n) if (S[i]) FE(j, 1, n) if (!T[j]) a = min(a, Lx[i] +Ly[j] - W[i][j]); FE(i, 1, n) { if (S[i]) Lx[i] -= a; if (T[i]) Ly[i] += a; }}void KM(){ FE(i, 1, n) { Left[i] = Lx[i] = Ly[i] = 0; FE(j, 1, n) Lx[i] = max(Lx[i], W[i][j]); } FE(i, 1, n) { while (1) { FE(j, 1, n) S[j] = T[j] = 0; if (match(i)) break; else update(); } }}//稳定婚姻问题// LA3989 Ladies' Choice#include<cstdio>#include<queue>using namespace std;const int maxn = 1000 + 10;int pref[maxn][maxn], order[maxn][maxn], next[maxn], future_husband[maxn], future_wife[maxn];queue<int> q; // 未订婚的男士队列void engage(int man, int woman) { int m = future_husband[woman]; if(m) { future_wife[m] = 0; // 抛弃现任未婚夫(如果有的话) q.push(m); // 加入未订婚男士队列 } future_wife[man] = woman; future_husband[woman] = man;}int main() { int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) scanf("%d", &pref[i][j]); // 编号为i的男士第j喜欢的人 next[i] = 1; // 接下来应向排名为1的女士求婚 future_wife[i] = 0; // 没有未婚妻 q.push(i); } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { int x; scanf("%d", &x); order[i][x] = j; // 在编号为i的女士心目中,编号为x的男士的排名 } future_husband[i] = 0; // 没有未婚夫 } while(!q.empty()) { int man = q.front(); q.pop(); int woman = pref[man][next[man]++]; if(!future_husband[woman]) engage(man, woman); // woman没有未婚夫,直接订婚 else if(order[woman][man] < order[woman][future_husband[woman]]) engage(man, woman); // 换未婚夫 else q.push(man); // 下次再来 } while(!q.empty()) q.pop(); for(int i = 1; i <= n; i++) printf("%d\n", future_wife[i]); if(T) printf("\n"); } return 0;}
生成树问题
次小生成树-----最佳替代边
int dfs1(int u, int fa, int rt) //求点rt到 以u为根的数及其子树的最小距离{ REP(i, SZ(tree[u])) { int v = tree[u][i]; if (v != fa) dp[rt][u] = min(dp[rt][u], dfs1(v, u, rt)); } if (fa != rt) dp[rt][u] = min(dp[rt][u], dis[rt][u]); return dp[rt][u];}int dfs2(int u, int fa, int rt){// 求 以rt为根的数及其子树 到 以u为根的数及其子树的最小距离 int ans = dp[u][rt]; REP(i, SZ(tree[u])) { int v = tree[u][i]; if (v != fa) ans = min(ans, dfs2(v, u, rt)); } return ans;}void solve(){ REP(i, n) dfs1(i, -1, i); REP(i, n) { REP(j, SZ(tree[i])) { int v = tree[i][j]; if (d[i][v] != INF) continue; d[i][v] = d[v][i] = dfs2(v, i, i); } }}//////一次dfs版本int dfs(int u, int fa, int rt){//用同层节点去更新 int ans = INF; REP(i, SZ(G[u])) { int v = G[u][i]; if (v != fa) { int t = dfs(v, u, rt); ans = min(ans, t); dp[u][v] = dp[v][u] = min(dp[u][v], t); } } if (fa != rt) ans = min(ans, dis[rt][u]); return ans;}prim(); REP(i, n) dfs(i, -1, i);
次小生成树-----瓶颈路
void prim(){ mst = 0; REP(i, N) dis[i] = INF, fa[i] = -1; dis[0] = 0; CLR(cost, 0); int mi; int pos; REP(i, N) { mi = INF; REP(j, N) if (!vis[j] && dis[j] < mi) { mi = dis[j]; pos = j; } mst += mi; REP(j, N) if (vis[j] && fa[j] != -1) { cost[j][pos] = max(cost[j][fa[pos]], g[fa[pos]][pos]); cost[pos][j] = cost[j][pos]; } if (fa[pos] != -1) used[fa[pos]][pos] = used[pos][fa[pos]]= 1; vis[pos] = 1; REP(j, N) if (!vis[j] && dis[j] > g[pos][j]) { dis[j] = g[pos][j]; fa[j] = pos; } }<p>}</p>
kruskal求次小生成树 + 瓶颈路
//maxcost[i][j]为i->j的瓶颈路//对于MST边u, v maxcost[u][v] = 0int n, m, x[maxn], y[maxn], p[maxn];int pa[maxn];int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; }//G保存MST C保存MST边权vector<int> G[maxn];vector<double> C[maxn];struct Edge { int x, y; double d; bool operator < (const Edge& rhs) const { return d < rhs.d; }};Edge e[maxn*maxn];double maxcost[maxn][maxn];vector<int> nodes;void dfs(int u, int fa, double facost) { for(int i = 0; i < nodes.size(); i++) { int x = nodes[i]; maxcost[u][x] = maxcost[x][u] = max(maxcost[x][fa], facost); } nodes.push_back(u); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(v != fa) dfs(v, u, C[u][i]); }}double MST() { sort(e, e+m); for(int i = 0; i < n; i++) { pa[i] = i; G[i].clear(); C[i].clear(); } int cnt = 0; double ans = 0; for(int i = 0; i < m; i++) { int x = e[i].x, y = e[i].y, u = findset(x), v = findset(y); double d = e[i].d; if(u != v) { pa[u] = v; G[x].push_back(y); C[x].push_back(d); G[y].push_back(x); C[y].push_back(d); ans += d; if(++cnt == n-1) break; } } return ans;}
prim求次小生成树
//use[u][v] = 2时,边<u, v>在MST上//use[u][v] = 1时,原图存在边<u, v>。//f[u][v]表示u->v的最小瓶颈路 初始化为0double prim(){ int pre[maxn] = {-1}; bool vis[maxn] = {0}; double d[maxn], ret = 0; FF(i, 1, n+1) d[i] = INF; d[1] = 0; FF(i, 1, n+1) { int pos; double tmp = INF; FF(j, 1, n+1) if(!vis[j] && d[j] < tmp) tmp = d[j], pos = j; if(pre[pos] != -1) { use[pre[pos]][pos] = use[pos][pre[pos]] = 2; FF(j, 1, n+1) if(vis[j]) f[pos][j] = f[j][pos] = max(f[j][pre[pos]], g[pre[pos]][pos]); } vis[pos] = 1; ret += d[pos]; FF(j, 1, n+1) if(!vis[j] && use[pos][j] && g[pos][j] < d[j]) d[j] = g[pos][j], pre[j] = pos; } return ret;}
斯坦纳树--spfa+状态压缩
hdu4085 Peach Blossom Springint n, m, k, st;int mask[maxn], dp[Maxs], dis[maxn][Maxs];bool inq[maxn][Maxs];struct Edge{ int u, v, w;};vector<Edge> edges;VI G[maxn];queue<int> Q;void add(int u, int v, int w){ edges.PB((Edge){u, v, w}); edges.PB((Edge){v, u, w}); int sz = edges.size(); G[u].PB(sz - 2); G[v].PB(sz - 1);}void init(){ CLR(dp, 0x3f), CLR(dis, 0x3f); CLR(mask, 0), CLR(inq, 0); RIII(n, m, k); edges.clear(); FE(i, 0, n + 1) G[i].clear(); st = 1 << 2 * k; REP(i, m) { int u, v, w; RIII(u, v, w); add(u, v, w); }}void spfa(){ while (!Q.empty()) { int u = Q.front() / 10000, s0 = Q.front() % 10000; inq[u][s0] = 0; Q.pop(); REP(i, SZ(G[u])) { Edge e = edges[G[u][i]]; int v = e.v, ns = s0 | mask[e.v]; if (dis[v][ns] > dis[u][s0] + e.w) { dis[v][ns] = dis[u][s0] + e.w; if (ns == s0 && !inq[v][ns]) { inq[v][ns] = 1; Q.push(v * 10000 + ns); } } } }}bool check(int s){ int a = 0; REP(i, k) { if (s & (1 << i)) a++; if (s & (1 << (k + i))) a--; } return a == 0;}void solve(){ FE(i, 1, k)////初始化 { mask[i] = 1 << (i - 1), dis[i][mask[i]] = 0; mask[n - i + 1] = 1 << (k + i - 1), dis[n - i + 1][mask[n - i + 1]] = 0; } REP(s, st)////枚举集合 { FE(i, 1, n) { for (int s0 = (s - 1) & s ; s0; s0 = (s0 - 1) & s)///枚举子集,通过子树划分转移状态 dis[i][s] = min(dis[i][s], dis[i][s0 | mask[i]]+dis[i][(s - s0) | mask[i]]); if (dis[i][s] < INF && !inq[i][s]) { inq[i][s] = 1; Q.push(i * 10000 + s); } } spfa();///spfa转移状态 } REP(s, st) FE(i, 1, n) dp[s] = min(dp[s], dis[i][s]); REP(s, st) if (check(s)) { for (int s0 = s & (s - 1); s0; s0 = s & (s0 - 1)) if (check(s0)) dp[s] = min(dp[s0] + dp[s - s0], dp[s]); } if (dp[st - 1] >= INF) puts("No solution"); else WI(dp[st - 1]);}int main(){ int T; RI(T); while (T--) { init(); solve(); }}
生成树计数问题
////给定一个无向图G,求它生成树的个数tint degree[N];LL C[N][N];LL det(LL a[][N], int n)//生成树计数:Matrix-Tree定理{ LL ret=1; for(int i=1; i<n; i++) { for(int j=i+1; j<n; j++) while(a[j][i]) { LL t=a[i][i]/a[j][i]; for(int k=i; k<n; k++) a[i][k]=(a[i][k]-a[j][k]*t); for(int k=i; k<n; k++) swap(a[i][k],a[j][k]); ret=-ret; } if(a[i][i]==0) return 0; ret=ret*a[i][i]; } if(ret<0) ret=-ret; return ret;}void solve(){ memset(degree,0,sizeof(degree)); memset(C,0,sizeof(C)); scanf("%d%d",&n,&m); while(m--) { scanf("%d%d",&u,&v); u--; v--; C[u][v]=C[v][u]=-1; degree[u]++; degree[v]++; } for(int i=0; i<n; ++i) C[i][i]=degree[i]; printf("%lld\n",det(C,n));}
固定根的最小树型图,邻接矩阵写法
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; }};
TwoSAT问题
两数AND为1
sat.add_clause(x, 1, x, 1);
sat.add_clause(y, 1, y, 1);
AND为0
sat.add_clause(x, 0, y, 0);
两数OR为1
sat.add_clause(x, 1, y, 1);
两数OR为0
sat.add_clause(x, 0, x, 0);
sat.add_clause(y, 0, y, 0);
两数XOR为1
sat.add_clause(x, 0, y, 0);
sat.add_clause(x, 1, y, 1);
两数XOR为0
sat.add_clause(x, 1, y, 0);
sat.add_clause(x, 0, y, 1);
sat.add_clause(x, 1, x, 1);
sat.add_clause(y, 1, y, 1);
AND为0
sat.add_clause(x, 0, y, 0);
两数OR为1
sat.add_clause(x, 1, y, 1);
两数OR为0
sat.add_clause(x, 0, x, 0);
sat.add_clause(y, 0, y, 0);
两数XOR为1
sat.add_clause(x, 0, y, 0);
sat.add_clause(x, 1, y, 1);
两数XOR为0
sat.add_clause(x, 1, y, 0);
sat.add_clause(x, 0, y, 1);
struct TwoSAT{ int n; vector<int> 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)); }void add_clause(intx, int xval, int y, int yval){/////x = xval OR y = yval 即不能同时取!val与!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;};
5 0
- 图论模板整理
- 图论算法模板整理
- 图论算法模板整理
- 图论算法模板整理
- 模板整理
- 模板整理---大数模板
- 模板整理: 图论---二分图匹配
- 【整理】Eclipse注释模板
- 排序模板(整理)
- 网站模板整理
- Templat 模板(整理)
- ECshop模板机制整理
- NOIP 考前模板整理
- 模板整理之KMP
- ACM模板整理
- 【整理】Eclipse注释模板
- 背包整理模板
- 高斯消元模板整理
- 简洁清爽全屏自适应Jquery幻灯片
- ios学习之 关于Certificate、Provisioning Profile、App ID的介绍及其之间的关系
- HDU 3790 最短路
- 《0bug-C/C++商用工程之道》节选01--内存栈-1
- Tomcat如何与Servlet对象进行交互
- 图论模板整理
- 技术管理的核心内容——提高团队技能
- Oracle数据导入时,字符集问题解决
- java 读取、修改properties
- DIV+CSS里display:block的作用
- 在程序员的眼里,用户是这样使用他们开发的软件的
- Android应用程序安装过程源代码分析
- svn命令行说明
- WebService几种实现方式