2020 10 .31node
內容來自OI-wiki和yu__xuan 的講課ios
強聯通份量(我的理解):c++
在一個無向圖中,u 能到v ,v 也能到ugit
在有向圖中u能到v,v也能到u那麼很顯然 , u---v能夠構成一個環算法
強聯通份量就是把這個點集縮成一個點,因而乎,這個圖變成了一個DAG(有向無環圖) 而後你就可在這個新圖上根據DAG的性質開始作題,好比 拓撲網絡
tarjan 算法是由棧來實現的spa
dfn[u] ---->dfs序 low[u] ---->以u爲根的子樹,最小的dfs序
那麼會出現 3 中狀況:.net
v 未被訪問:繼續對 v 進行深度搜索。在回溯過程當中,用 low[v] 更新 low[u]。由於存在從 u 到 v 的直接路徑,因此 v 可以回溯到的已經在棧中的結點,u 也必定可以回溯到。code
v 被訪問過,還在棧中:即已經被訪問過,根據low的定義(可以回溯到的最先的已經在棧中的結點),則用dfn[v]更新low[u]。get
v 被訪問過,已不在棧中:說明 v 所在的強連通份量已經找出,不可能和 u 在一個強連通份量中,因此不用操做。
by---yu__xuan學姐
void tarjan(int u){ st[++sn]=u,vis[u]=1; dfn[u]=low[u]=++cnt; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!dfn[v]){ tarjan(v),low[u]=min(low[u],low[v]); } else if(vis[v]==1) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ int top=st[sn--];vis[top]=0; scc++; siz[scc]++,num[top] = scc; while(top!=u){ top=st[sn--]; vis[top]=0; num[top] = scc,siz[scc]++; } } }//變量註釋在下一個板子裏面
另外一種短一點的板子
void tarjan(int u){ dfn[u] = low[u] = ++cnt; st[++sn] = u,vis[u] = 1; for(int i = head[u] ; i ; i = e[i].next){ int to = e[i].to; if(!dfn[to]) low[u] = min(low[u],low[to]); else if(vis[to]) low[u] = min(low[u], dfn[to]); //if(vis[to]) low[u] = min(low[u], low[to]); //這兩種寫法都對 //可是對於割點(頂)來講就只有第一種寫法對了,能夠看我割點的博客 } if(low[u] == dfn[u]){ int top ,scc++;//scc--第幾個強聯通份量 do{ top = st[sn--]; vis[top] = 0; num[top] = scc; siz[scc]++; //top所對應的第幾個強聯通份量,這個強聯通份量裏面有幾個點 //上一個板子能夠跟着這個修改一下 }while(top != u); } }
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cstring> using namespace std; const int M=5e4+10; const int N=1e4+10; int n,m,nume,head[N]; int st[M],sn,cnt,dfn[N],low[N]; bool vis[N]; struct node{ int to,next; }e[M<<1]; void add_edge(int from,int to){ e[++nume].next = head[from]; e[nume].to=to; head[from]=nume; } bool falg=0; int ans; void tarjan(int u){ st[++sn]=u,vis[u]=1; dfn[u]=low[u]=++cnt; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(dfn[v]==0){ tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]==1) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ int top=st[sn--]; vis[top]=0; while(top!=u){ top=st[sn--]; vis[top]=0; falg=1; } } if(falg==1) falg=0,ans++; } int main(){ scanf("%d%d",&n,&m); for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); add_edge(u,v); //add_edge(v,u); }for(int i=1;i<=n;i++){ if(dfn[i]!=0) continue; tarjan(i); } printf("%d",ans); return 0; }
修一下之前寫的博客,之前寫的真是慘不忍睹如今也是加兩道有意思的題
solution:
code:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #include <queue> #define ll long long using namespace std; const int N = 1e4 + 100; const int inf = 1e9; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct Edge { int from, to, net; } e[N * N] ; int head[N], nume; void add_edge(int from, int to) { e[++nume] = (Edge){from, to, head[from]}; head[from] = nume; } int st[N], sn, vis[N]; int dfn[N], low[N], cnt; int scc,rd[N], cd[N],minn[N]; int cost[N]; int num[N]; void tir(int x) { st[++sn] = x, vis[x] = 1; low[x] = dfn[x] = ++cnt; for (int i = head[x]; i; i = e[i].net) { int to = e[i].to; if (!dfn[to]) tir(to), low[x] = min(low[x], low[to]); else if (vis[to]) low[x] = min(low[x], dfn[to]); } if (dfn[x] == low[x]) { int top = st[sn--]; vis[top] = 0;scc++; minn[++scc] = inf; minn[scc] = min(minn[scc],cost[top]); num[top] = scc; while (top != x) { top = st[sn--]; vis[top] = 0; minn[scc] = min(minn[scc],cost[top]); num[top] = scc; } } } int main() { int n = read() , p = read(); for(int i = 1 ; i<= n; i++) cost[i] = inf; for (int i = 1; i <= p; i++) { int x = read(); cost[x] = read(); } int r = read(); for(int i = 1 ; i <= r ; i++) { int u = read() , v = read(); add_edge(u ,v); } for (int i = 1; i <= n; i++) { if (!dfn[i] && cost[i] != inf) tir(i); } for(int i = 1 ; i<= n ;i++) { if(!dfn[i]) { puts("NO"); printf("%d",i); system("pause"); return 0; } } for(int i = 1 ; i <= nume ; i++) { if(num[e[i].from] == num[e[i].to]) continue; cd[num[e[i].from]]++; rd[num[e[i].to]]++; } int ans = 0; for(int i = 1 ; i <= scc ;i++) { if(rd[i] == 0) ans += minn[i]; } puts("YES"); printf("%d",ans); system("pause"); return 0; }
solution:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define ll long long using namespace std; const int N = 5e5 + 100; const int inf = 1e9; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct Edge { int from, to, net; } e[N], G[N]; int head[N], nume; void add_edge(int from, int to) { e[++nume] = (Edge){from, to, head[from]}; head[from] = nume; } int headg[N], numg; void another(int from, int to) { G[++numg] = (Edge){from, to, headg[from]}; headg[from] = numg; } int st[N], sn, vis[N]; int dfn[N], low[N], cnt; int scc, rd[N], cd[N], sum[N]; int val[N], flag[N], FLAG[N]; int num[N]; int n, m, s, p; int start; void tir(int x) { st[++sn] = x, vis[x] = 1; low[x] = dfn[x] = ++cnt; for (int i = head[x]; i; i = e[i].net) { int to = e[i].to; if (!dfn[to]) tir(to), low[x] = min(low[x], low[to]); else if (vis[to]) low[x] = min(low[x], dfn[to]); } if (dfn[x] == low[x]) { int top = st[sn--]; vis[top] = 0; scc++; if (top == s) start = scc; FLAG[scc] += flag[top]; sum[scc] += val[top]; num[top] = scc; while (top != x) { top = st[sn--]; if (top == s) start = scc; vis[top] = 0; sum[scc] += val[top]; num[top] = scc; FLAG[scc] += flag[top]; } } } int ans, dis[N]; bool VIS[N]; void spfa() { queue<int> q; q.push(start); VIS[start] = 1; dis[start] = sum[start]; while (!q.empty()) { int fr = q.front(); q.pop(); VIS[fr] = 0; for (int i = headg[fr]; i; i = G[i].net) { int to = G[i].to; if (dis[to] < dis[fr] + sum[to]) { dis[to] = dis[fr] + sum[to]; if (!VIS[to]) { q.push(to), VIS[to] = 1; } } } } } int main() { n = read(), m = read(); for (int i = 1; i <= m; i++) { int u = read(), v = read(); add_edge(u, v); } for (int i = 1; i <= n; i++) val[i] = read(); s = read(), p = read(); for (int i = 1; i <= n; i++) { if (!dfn[i]) tir(i); } for (int i = 1; i <= p; i++) { int x = read(); flag[x] = 1; } for (int i = 1; i <= m; i++) { if (num[e[i].from] == num[e[i].to]) continue; another(num[e[i].from], num[e[i].to]); rd[num[e[i].to]]++; } spfa(); for (int i = 1; i <= n; i++) { if(flag[i]) ans = max(ans, dis[num[i]]); // cout << dis[i] << " "; } printf("%d", ans); system("pause"); return 0; }