hdu 4635 强连通缩点
1 /* 2 题意:给出简单有向图的定义:不能有自环,没有重边,不能强连通; 3 问给出一个初始图,最多加多少条边可以使得这个图满足上述定义。 4 5 题解:强连通缩点 6 首先没有自环,没有重边都十分清晰,然后不能强连通给了一个提示,就是要找出连通分量,那么怎样才能加最多的边 7 而使得这个图不是强连通,只需保证将这个图分成两个点集,且其中的一个点集到另一点集只能含有单向的边,然后这 8 两个点集之内的点是强连通,就保证了最优情况的非强连通,由于有一些边是已存在,因此对于加的边的数目来说要最 9 优的话,必须找出加边最多的情况;然后需要推出加边的公式: 10 ans = N - m - a * b 11 其中N是指整个图为竞赛图时的总边数,然后已经存在的边为m需要减去,然后就是找出a*b,a,b是指将图分成两个点集 12 之后两个点集的点的数量,理由:分成两个点集之后,一个点集到另一个点集需要的只有单向边,因此要将所有的反向 13 边都删除,因为是竞赛图,因此所要去除的边的数目即为a*b,减去之后求出的解就是加边数目,而要使得ans最小,则 14 a*b最小即可;通过强连通缩点将不可能的点集合成一个点,因为从这些点挑选出的部分点集必然会导致分出的两个点集 15 有回路,因此强连通分量需要选取所有的点,然后得出的一个缩点后的图就是一个拓扑图,我们要选取的应该是a尽量小 16 的点集,b=(n-a),那么选一个包含点数目最小的点集就是好了,因此应该选择出度或入度为0的点集,因为如果选择的 17 是出入度均不为0的点集作为a点集,由于有已存在的边不能删除则必然是存在边返回剩余点集,那样不符合要求,因此 18 需要选择出入度为0的最小点集。 19 */ 20 #include <cstdio> 21 #include <cstring> 22 23 #define clr(a,b) (memset(a,b,sizeof(a))) 24 #define cpy(a,b) (memcpy(a,b,sizeof(a))) 25 const int NV = 100005; 26 const int NE = 100005; 27 28 inline int Min(int a, int b) {return a < b ? a : b;} 29 inline int Max(int a, int b) {return a > b ? a : b;} 30 int deep, scc, top, SZ, n; 31 struct edge 32 {int v, next;} E[NE]; 33 int head[NV], dfn[NV], low[NV], id[NV], st[NV]; 34 int indegree[NV],outdegree[NV]; 35 int mindegree[NV],moutdegree[NV]; 36 int sum[NV]; 37 bool in[NV]; 38 39 inline void init(int _n) { 40 n = _n; 41 clr(head, -1); 42 clr(dfn, -1); 43 clr(in,false); 44 deep = scc = SZ = top = 0; 45 } 46 void tarjan(int u) { 47 int v, i; 48 dfn[u] = low[u] = ++ deep; 49 st[top++] = u; 50 in[u] = true; 51 for (i = head[u]; i != -1; i = E[i].next) { 52 v = E[i].v; 53 if (dfn[v] == -1) { 54 tarjan(v); 55 low[u] = Min(low[u], low[v]); 56 } 57 else if (in[v]) { 58 low[u] = Min(low[u], dfn[v]); 59 } 60 } 61 if (low[u] == dfn[u]) { 62 int tsum = 0; 63 do { 64 tsum ++; 65 v = st[--top]; 66 in[v] = false; 67 id[v] = scc; // 缩点 68 } while (u != v); 69 sum[scc] = tsum; 70 scc ++; 71 } 72 } 73 inline void insert(int u, int v) { 74 E[SZ].v = v; 75 E[SZ].next = head[u]; 76 head[u] = SZ ++; 77 } 78 inline void solve() { 79 for (int i = 1; i <= n; i++) { 80 if (dfn[i] == -1) { 81 tarjan(i); 82 } 83 } 84 } 85 86 int main(void) 87 { 88 int t,n,m; 89 scanf("%d",&t); 90 for(int cas = 1; cas <= t; cas++) 91 { 92 scanf("%d%d",&n,&m); 93 init(n); 94 clr(indegree,0); 95 clr(outdegree,0); 96 clr(sum,0); 97 for(int i=0; i<m; i++) 98 { 99 int u,v; 100 scanf("%d%d",&u,&v); 101 insert(u,v); 102 } 103 solve(); 104 if (scc == 1) 105 { 106 printf("Case %d: -1\n",cas); 107 continue; 108 } 109 110 for(int i=1; i<=n; i++) 111 { 112 for (int j = head[i]; j != -1; j = E[j].next) 113 { 114 if (id[i] != id[E[j].v]) 115 outdegree[id[i]]++,indegree[id[E[j].v]]++; 116 } 117 } 118 119 int tans = n*n; 120 for(int i=0; i<scc; i++) 121 if (indegree[i] == 0 || outdegree[i] == 0) 122 tans = Min(tans, sum[i]*(n-sum[i])); 123 printf("Case %d: %d\n",cas,n*(n-1)-m-tans); 124 } 125 return 0; 126 }