POJ 1236 Network of Schools 強連通分量+縮點
阿新 • • 發佈:2018-12-18
題意:問,對於一個DAG(又向無環圖): 1.至少要選幾個點,才能從這些點出發到達所有點 2.至少加入幾條邊,就能從圖中任何一個點出發到達所有點
根據有用定理:有向無環圖中所有入度不為0的點,一定 可以由某個入度為0的點出發可達。 (由於無環,所以從任何入度不為0的 點往回走,必然終止於一個入度為0的 點)
先求DAG的強連通分量數,再縮點,可以用tarjan演算法來做。
第一個問題:不難想到答案就是縮點之後入度為0的點的個數
第二個問題:設縮點後入度為0的個數是n,出度為0的個數是m,至少新增邊的條數就是max(n,m)
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<algorithm> #include<vector> #include<stack> #include<string.h> using namespace std; const int maxn=1100; int dfn[maxn],low[maxn],vis[maxn],color[maxn],Stack[maxn]; int n,index,len,cnt; vector<int>g[maxn]; int in[maxn],out[maxn],vis1[maxn]; void tarjan(int u) { dfn[u]=low[u]=++index; Stack[cnt++]=u;//cnt++ 需要注意 vis[u]=1; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); }else if(vis[v]==1) { low[u]=min(dfn[v],low[u]); } } if(dfn[u]==low[u]) { ++len; int v; do{ v=Stack[--cnt];//--cnt 需要注意 color[v]=len; //縮點,把屬於強連通分量裡的點染上顏色,這樣相通顏色的點看成一個就好了 vis[v]=0; }while(u!=v); } } void init() { memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low)); memset(Stack,0,sizeof(Stack)); memset(color,0,sizeof(color)); memset(g,0,sizeof(g)); //g.clear(); //g.resize(maxn+1); for(int i=1;i<=n;i++) { g[i].clear(); } cnt=index=len=0; } int main() { while(scanf("%d",&n)!=EOF) { init(); for(int i=1;i<=n;i++) { int m; while(scanf("%d",&m) && m) g[i].push_back(m); } for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i);//tarjan還是老套路 } memset(out,0,sizeof(out)); memset(in,0,sizeof(in)); for(int i=1;i<=n;i++) //搜尋每一個邊,如果兩點間顏色不同的話,那麼就證明不是同一個連通分量裡的, //這樣就記錄點的出度與入度就好了 { for(int j=0;j<g[i].size();j++) { if(color[i]!=color[g[i][j]] ) { out[color[i]]++; //分別將點的出度 in[color[g[i][j]]]++;//點的入讀記錄一下 } } } int ans1=0; int ans2=0; for(int i=1;i<=len;i++) { if(in[i]==0) ans1++; if(out[i]==0) ans2++; } ans2=max(ans1,ans2); if(len==1) //如果縮點之後只有一個點了,那麼這個點就是全部,也不用再加邊了。 printf("1\n0\n"); else printf("%d\n%d\n",ans1,ans2); } return 0; }