Link: http://poj.org/problem?id=1236
/******** HOW TO SOLVE *******
利用tarjan算出强连通图的集合,把一个强连通图看做一个节点,
算出每个节点之间的路径(即强连通图之间的路径)
设入度为0的点个数为fromNum,出度为0的点个数为outNum
答案即 fromNum 和 max(outNum, fromNum)
注意只有一个强连通图的特殊情况!!
******** HOW TO SOLVE ********/
/******* TO PROVE ********
<1>弱连通的有向图如果没有出度或入度为0的节点则它必强连通
=>弱连通的有向图所有节点的入度等于所有节点的出度。任给两个节点u,v,可以证明必有u到v的路。
<2>有向图入度,出度为0的节点数分别为Iz,Oz.则通过给它加边,使它变成强连通图,最少要加max{Iz,Oz}条边。
=>考虑将Oz个出度为0的节点与Iz个入度为0的节点相连接,若Oz!=Iz则将多出来的再接到其它节点上,以消除出度或入度为0的节点。即最少需加max{Iz, Oz}条边才能变成没有出度或入度为0的节点的有向图。
对于有多个弱连通分量的有向图,比如图G有Gx, Gy两个弱连通分量,其入度,出度为0的节点数分别为Ix,Ox和Iy,Oy. 不妨设Ox>=Iy,则考虑将Gx的Ox个出度为0的节点中的Iy个连到Gy的Iy个入度为0的节点上。这样就成了一个弱连通的有向图,它的入度,出度为0的节点数分别为Ix,Ox-Iy+Oy.则它的最少加边数为Iy+max{Ix,Ox-Iy+Oy},即为max{Ix+Iy,Ox+Oy}
posted by happynp
******* TO PROVE *********/
/***** NOTE ************
TLE 可能因为输入输出while循环无法跳出
反正一切while 或for 一直循环就会TLE
tarjan() 因为用多次 记得一开始vis instack都要赋值。。。
vis instack这种记录状态的数组 记得在状态改变时候 再次赋值!!!
代码里面有 邻接表的 也有邻接矩阵的实现 都AC 的
而且我看过了 时间和空间都没有区别 可能有会区别但是oj差别太小没显示出来吧。
邻接表比较适合稀疏图吧。。。
***** NOTE ***********/


#include <cstdio> #include <iostream> #include <stack> using std::stack ; const int maxn = 105; int n, ConnCnt, index, rounds, dfn[maxn], low[maxn], conn[maxn][maxn]; int belong[maxn], out[maxn], from[maxn], map[maxn][maxn]; bool inStack[maxn], vis[maxn]; struct node { int receive; node *next; }school[maxn]; stack<int> s; int min(int a, int b) { return a <= b ?a :b ; } void init() { ConnCnt = 0, index = 1; for(int i=1; i<=n; i++) { school[i].receive = i; school[i].next = NULL; } } void tarjan( int u ) { int i; dfn[u] = low[u] = index ++ ; s.push(u); inStack[u] = 1; vis[u] = 1; node *p = school[u].next; while( p!= NULL ) { int v = p->receive ; if(!vis[v]) { vis[v] = 1; tarjan(v); low[u] = min(low[u], low[v]); } else if(inStack[v]) low[u] = min(low[u], dfn[v]); p = p->next; } /* for(int v=1; v<=n; v++) { if(map[u][v] && !vis[v]) { vis[v] = 1; tarjan(v); low[u] = min(low[u], low[v]); } else if(map[u][v] && inStack[v]) low[u] = min(low[u], dfn[v]); } */ if(dfn[u] == low[u]) { int size = 0; ConnCnt ++ ; while(i=s.top()) { // conn[ConnCnt][++size] = i; belong[i] = ConnCnt; inStack[i] = 0; s.pop(); if( i == u ) break; } } } int check() { for(int i=1; i<=n; i++) if(!vis[i]) return i; return 0; } int main() { int i, var; scanf("%d", &n); init(); for(i=1; i<=n; i++) { while(scanf("%d", &var), var!=0 ) { map[i][var] = 1; node *p = (node *)malloc(sizeof(node)); p->receive = var; p->next = school[i].next; school[i].next = p; } } while(i = check()) { tarjan(i); } for(i=1; i<=n; i++) { node *p = school[i].next; while( p != NULL ) { if(belong[p->receive] != belong[i]) { out[belong[i]]++, from[belong[p->receive]]++; } p = p->next; } } /* for(i=1; i<=n; i++) for(int j=1; j<=n; j++) //有向图不是i=j开始 { if(map[i][j] && belong[i] != belong[j]) out[belong[i]]++, from[belong[j]]++; }*/ int outNum = 0, fromNum = 0; for(i=1; i<=ConnCnt; i++) { if(out[i] == 0) outNum ++; if(from[i] == 0) //有可能入度为0 出度也为0 不用else if fromNum ++; } int max = outNum>=fromNum ?outNum :fromNum ; // int minNum = outNum+fromNum - max; if(ConnCnt == 1) printf("1\n"); else printf("%d\n", fromNum); if(ConnCnt == 1) printf("0\n"); else printf("%d\n", max); return 0; }