D15 SCC 缩点 Tarjan 算法
D15 SCC 缩点 Tarjan 算法_哔哩哔哩_bilibili
P2812 校园网络【[USACO]Network of Schools加强版】 - 洛谷
图问题:有向无向?有环无环?连通不连通?正权负权?
1. 把SCC缩成一个点,旧点 x → 缩点 scc[x],n → cnt
2. 统计缩点的入度、出度,if(scc[x]!=scc[y])
3. 构造答案
#include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,a,b; vector<int> e[N]; int dfn[N],low[N],tim,stk[N],top,scc[N],cnt; int din[N],dout[N]; //SCC的入度,出度 void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int y : e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(1){ int y=stk[top--]; scc[y]=cnt; if(y==x) break; } } } int main(){ cin >> n; for(int i=1, a; i<=n; i++) while(cin >> a, a) e[i].push_back(a); for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i); for(int x=1; x<=n; x++) for(int y:e[x]) if(scc[x]!=scc[y]){ din[scc[y]]++; dout[scc[x]]++; } int a=0, b=0; for(int i=1; i<=cnt; i++){ if(!din[i]) a++; if(!dout[i]) b++; } cout<<a<<"\n"; if(cnt == 1) cout<<"0"; else cout<<max(a,b); }
P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷
#include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,a,b; vector<int> e[N]; int dfn[N],low[N],tot,stk[N],top,scc[N],siz[N],cnt; int dout[N]; //SCC的出度 void tarjan(int x){ dfn[x]=low[x]=++tot; stk[++top]=x; for(int y:e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(1){ int y=stk[top--]; scc[y]=cnt; ++siz[cnt]; if(y==x) break; } } } int main(){ cin>>n>>m; while(m--){ cin>>a>>b; e[a].push_back(b); } for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i); for(int x=1; x<=n; x++) for(int y : e[x]) if(scc[x]!=scc[y]) ++dout[scc[x]]; int sum=0,zeros=0; for(int i=1; i<=cnt; i++) if(dout[i]==0){ sum=siz[i]; ++zeros; //出度为0的SCC的个数 } if(zeros>1) sum=0; cout<<sum<<endl; }
1. 缩点
2. 对缩点建拓扑图(注意编号逆序)
3. 对拓扑图求最长路
#include<bits/stdc++.h> using namespace std; const int N=100010; int n,m,a,b; vector<int> e[N],ne[N]; int dfn[N],low[N],tim,stk[N],top,scc[N],cnt; int w[N],nw[N],d[N]; void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int y : e[x]){ if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(1){ int y=stk[top--]; scc[y]=cnt; if(y==x) break; } } } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]; for(int i=1;i<=m;i++){ cin>>a>>b; e[a].push_back(b); } for(int i=1;i<=n;i++) //缩点 if(!dfn[i]) tarjan(i); for(int x=1;x<=n;x++){ //建拓扑图 nw[scc[x]]+=w[x]; for(int y:e[x]) if(scc[x]!=scc[y]) ne[scc[x]].push_back(scc[y]); } for(int x=cnt;x;x--){ //求最长路 if(d[x]==0) d[x]=nw[x]; //起点 for(int y:ne[x]) d[y]=max(d[y],d[x]+nw[y]); } int ans=0; for(int i=1;i<=cnt;i++) ans=max(ans,d[i]); cout<<ans; }
浙公网安备 33010602011771号