POJ 1236 Network Of Schools ( tarjan求强连通分量 + 缩点成DAG图 )
来源:互联网 发布:excel数据合并汇总 编辑:程序博客网 时间:2024/05/17 17:57
/*** tarjan + 缩点:* 这题题意其实就是求:强连通分量数(task1) 和 在对强连通分量缩点后形成的* DAG图加至少多少条边,能使其成为一个强连通图(也就是题目所说的任何一个点出* 去都能到图中任何一个点。)* 怎么算强连通分量,怎么缩点。 其实tarjan算法就行,理解tarjan后,把所有点* 都可以用数组belong去分配到某一个强连通分量中去。 缩点就是把每个强连通分量* 看成一个节点和剩下的不能再构成强连通分量的有向边就构成了一个有向无环图(DAG)* 然后对各点计算他的出度入度。* 在完成tarjan和缩点之后。。就是怎么理解题意了。* 任务一:不改变原图,至少要发多少个软件。。在一个强连通分量里发一个就能连到* 这个强连通分量里的任一节点。 这里就体现了为什么缩点,每个强连通分量只要有个软件传来* 就可以遍历所有节点了。 所以只要考虑之后的DAG图就行了。如果在每个入度为0的地方放* 个软件,是不是就可以到达能够连通的所有缩点? 这时候其实就是计算有多少个入度为0的缩点** 任务二: 如何加边使得,从任何一个缩点出发都能到达其余任何点。 这其实就是之前说的* 加边让原来的DAG图变成一个强连通图。* 关键是怎么加边。。首先一个强连通图肯定是不存在一个点只有入度或者只有出度的。* 如果能够让每个点的入度=出度(把一个出度为零的点加条边指向入度为零的那个点,不就是最大的* 发挥了这条边的作用了吗),再通过把原来的分离的DAG图相连接就行了。* 换句话说,我只要找出出度为零 和 入度为零中的较大者就是task2的答案了。这个可以自己画画图。*/#include <cstdio>#include <cstring>#include <cmath>#include <string>#include <vector>#include <algorithm>#define DEBUG 0#define INF 0x7fffffff#define MAXS 101typedef long long LL;using namespace std;int gra[MAXS][MAXS], dfn[MAXS], low[MAXS], st[MAXS], vis[MAXS], belong[MAXS];int in[MAXS], out[MAXS];int n, rear, numOfSCC, dfs_colock;void init() { rear = 0; numOfSCC = dfs_colock = 1; memset(gra, 0, sizeof(gra)); for(int i = 0; i <= n; i ++ ) { in[i] = out[i] = vis[i] = low[i] = 0; dfn[i] = -1; belong[i] = 0; }}void tarjan(int x) { dfn[x] = low[x] = dfs_colock ++; st[rear ++] = x; vis[x] = 1; for(int i = 1; i <= n; i ++) { if(!gra[x][i]) continue; if(dfn[i] == -1) { tarjan(i); low[x] = min(low[x], low[i]); } else if(vis[i]) { low[x] = min(low[x], dfn[i]); } } /** 在进行完对其子孙节点的tarjan后,也就是更新最小值后,low[x]仍然没变,说明x就是个强连通分量的根节点。*/ if(low[x] == dfn[x]) { int t; while(true) { t = st[-- rear]; belong[t] = numOfSCC; vis[t] = 0; if(t == x) break; } numOfSCC ++; }}void solve() { for(int i = 1; i <= n; i ++) { if(dfn[i] == -1) tarjan(i); } /** 缩点。*/ for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { if(gra[i][j] && belong[i] != belong[j]) { out[belong[i]] ++; in[belong[j]] ++; } } } /** 计算缩点后的DAG图中各节点的出入度。 */ int numIn = 0, numOut = 0; for(int i = 1; i < numOfSCC; i ++) { if(!in[i]) numIn ++; if(!out[i]) numOut ++; } if(numOfSCC == 2) printf("1\n0\n"); else { printf("%d\n", numIn); printf("%d\n", max(numIn, numOut)); }}int main(){ while(scanf("%d", &n) != EOF) { init(); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { int cur; scanf("%d", &cur); if(!cur) break; gra[i][cur] = 1; } } solve(); } return 0;}