/******************************************************************/
以下题解来自互联网:Juny的博客
思路核心:给你的闭包其实就是一个有向图;
方法:
1,对此图进行缩点,对于点数为n(n>1)的强连通分量最少要 n 条边,
对点数为 1 的强连通不需要边,这样计算出边数 m1 ;
2,在缩点后的有向无环图中进行反floyd,如果有边a->b,b->c,a->c那么显然a->c可以去掉,
就这样一直去除这样的边,直到不能再去为止,算出最终边数 m2;
3,m1+m2 即为答案;
这样做速度比较慢,但小草还没想出其他好的办法,希望有大牛指点……
/*****************************************************************/
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> using namespace std; #define MAXN 20010 #define MAXM 50010 struct Edge { int v, next; }edge[MAXM]; //边结点数组 int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM]; // first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组, //Belong[]为每个结点所对应的强连通分量标号数组 // Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号 int instack[MAXM],num[MAXN]; // instack[]为是否在栈中的标记数组 int n, m, cnt, scnt, top, tot; void init() { cnt = 0; scnt = top = tot = 0; memset(first, -1, sizeof(first)); memset(DFN, 0, sizeof(DFN)); memset(num,0,sizeof(num)); } void read_graph(int u, int v) { edge[tot].v = v; edge[tot].next = first[u]; first[u] = tot++; } void Tarjan(int v) { int t; DFN[v] = Low[v] = ++cnt; instack[v] = 1; stack[top++] = v; for(int e = first[v]; e != -1; e = edge[e].next) { int j = edge[e].v; if(!DFN[j]) { Tarjan(j); if(Low[v] > Low[j]) Low[v] = Low[j]; } else if(instack[j] && DFN[j] < Low[v]) { Low[v] = DFN[j]; } } if(DFN[v] == Low[v]) { scnt++; do { t = stack[--top]; instack[t] = 0; Belong[t] = scnt; //为缩点做准备的 num[scnt]++; }while(t != v); } } void solve() { for(int i = 1; i <= n; i++) if(!DFN[i]) Tarjan(i); } int main() { int i,j,map[210][210],sum1,ans,map1[210][210];//map1[][]是缩点后新建的图 while(scanf("%d",&n)!=EOF) { init(); ans=0; memset(map,0,sizeof(map)); memset(map1,0,sizeof(map1)); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&map[i][j]); if(map[i][j]==1&&i!=j) { read_graph(i,j); } } } solve(); sum1=0; for(i=1;i<=scnt;i++) { if(num[i]>1) sum1+=num[i]; } for(int ii=1;ii<=n;ii++) { for(int jj=1;jj<=n;jj++) { if(map[ii][jj]&&Belong[ii]!=Belong[jj]) map1[Belong[ii]][Belong[jj]]=1; } } for(int ii=1;ii<=scnt;ii++) for(int jj=1;jj<=scnt;jj++) for(int kk=1;kk<=scnt;kk++) if(map1[ii][jj]&&map1[ii][kk]&&map1[kk][jj])//此处在缩点新建图 map1[ii][jj]=0; for(int ii=1;ii<=scnt;ii++) for(int jj=1;jj<=scnt;jj++) if(map1[ii][jj]) ans++; printf("%d ",sum1+ans); } return 0; }