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 }

 

posted @ 2014-05-06 10:55  辛力啤  阅读(263)  评论(0编辑  收藏  举报