【tarjan+缩点】POJ1236[IOI1996]-Network of Schools
【题意】
见:http://blog.csdn.net/ascii991/article/details/7466278
【思路】
缩点+tarjan,思路也可以到上面的博客去看。(吐槽:这道题其实我没有AC。我过了当年IOI的数据,而把别人AC掉的程序带进去,明显过不了IOI的数据!求POJ修正一下!)我在这里引用一下:
找强连通分量,缩点。记f[i]为缩完点后的新图中各点入度,g[i]为出度,ans1为f[i]==0的点的数目,ans2为g[i]==0的点的数目则第一问为ans1,第二问则为max{ans1,ans2}。
至于第二问的解释,我的想法是对于得到的DAG图,考虑其中的出度为0的点和入度为0的点组成的点集V,将这些点相连,最多这需要max{ans1,ans2}条边,就能使整个图成为强连通分量。
但是请注意,大家可能都没发现,这个结论的前提是DAG图是连通的情况下才成立。如果DAG图有多个连通分量,则还要考虑将多个连通分量合并的所需代价。幸运的是,这道题保证了只有一个连通分量。(题目第一句话所说)
【错误点】
1.tarjan要进行多次,详见程序注释
2.循环从0开始还是从1开始要看清楚!
3.但只有一个连通分量的时候,不需要再增加边!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<vector> 6 #include<stack> 7 using namespace std; 8 const int MAXN=100+5; 9 int n;//学校的总数 10 int u[MAXN],v[MAXN];//记录每一条边的起始点和终点 11 int sp[MAXN];//记录缩点之后每个点对应的编号 12 int tot=0;//有向图中边的总数 13 int vis[MAXN]; 14 int instack[MAXN]; 15 int dfn[MAXN],low[MAXN]; 16 int indegree[MAXN],outdegree[MAXN];//记录缩点后的每一个点的入度与出度 17 vector<int> E[MAXN]; 18 stack<int> S; 19 int T=0; 20 int cnt=0;//为缩点后的每一个点编号 21 22 void tarjan(int u) 23 { 24 dfn[u]=low[u]=++T; 25 vis[u]=1; 26 S.push(u); 27 instack[u]=1; 28 29 for (int i=0;i<E[u].size();i++) 30 { 31 int son=E[u][i]; 32 if (!vis[son]) 33 { 34 tarjan(son); 35 low[u]=min(low[son],low[u]); 36 } 37 else 38 if (vis[son] && instack[son]) 39 low[u]=min(dfn[son],low[u]); 40 } 41 42 if (dfn[u]==low[u]) 43 { 44 cnt++; 45 int x; 46 do 47 { 48 x=S.top(); 49 S.pop(); 50 sp[x]=cnt; 51 instack[x]=0; 52 }while (x!=u); 53 } 54 } 55 56 void init() 57 { 58 memset(vis,0,sizeof(vis)); 59 memset(instack,0,sizeof(instack)); 60 scanf("%d",&n); 61 for (int i=1;i<=n;i++) 62 { 63 int to; 64 while (scanf("%d",&to) && to) 65 { 66 tot++; 67 u[tot]=i;v[tot]=to; 68 E[i].push_back(to); 69 } 70 } 71 } 72 73 void find() 74 { 75 memset(indegree,0,sizeof(indegree)); 76 memset(outdegree,0,sizeof(outdegree)); 77 if (cnt>1) 78 { 79 for (int i=1;i<=tot;i++) 80 /*错误点:写成了i=0;i<tot,导致有时候没有进入循环!*/ 81 { 82 if (sp[u[i]]!=sp[v[i]]) 83 { 84 outdegree[sp[u[i]]]++; 85 indegree[sp[v[i]]]++; 86 }//找出缩点后各点的出度和入度 87 } 88 89 int noin=0,noout=0; 90 for (int i=1;i<=cnt;i++) 91 { 92 if (indegree[i]==0) noin++; 93 if (outdegree[i]==0) noout++; 94 } 95 cout<<noin<<endl; 96 cout<<max(noin,noout)<<endl; 97 } 98 else if (cnt==1) printf("1\n\0\n"); 99 } 100 101 int main() 102 { 103 init(); 104 for (int i=1;i<=n;i++) if (vis[i]==0) tarjan(i); 105 /*错误点:一开始直接tarjan(1)了,导致有一些学校没有被访问到!每次随机从没有访问的某个结点开始!*/ 106 find(); 107 return 0; 108 }