Hdu--2767(强连通分量,缩点)

2014-10-14 02:19:23
思路:大白书的例题,挺好的强连通分量入门题。问题的大意就是问最少添加几条边能使一个有向图强连通。做法是,先求一遍强连通分量,然后把每个强连通分量缩点,这样实际上就形成了DAG,接下来只要判断每个缩点的入度和出度,找出入度为0的点个数a1,出度为0的点个数a2,然后取a1和a2的最大值,即是答案。(因为最终要使得每个缩点的入度、出度不为零,而添加一条边可以减少a1,也不可以不减少a1;可以减少a2,也可以不减少a2,那么最小答案就是max(a1,a2))

  1 /*************************************************************************
  2     > File Name: 2767.cpp
  3     > Author: Nature
  4     > Mail: 564374850@qq.com 
  5     > Created Time: Tue 14 Oct 2014 12:54:49 AM CST
  6 ************************************************************************/
  7 
  8 #include <cstdio>
  9 #include <cstring>
 10 #include <cstdlib>
 11 #include <cmath>
 12 #include <vector>
 13 #include <map>
 14 #include <set>
 15 #include <stack>
 16 #include <queue>
 17 #include <iostream>
 18 #include <algorithm>
 19 using namespace std;
 20 #define lp (p << 1)
 21 #define rp (p << 1|1)
 22 #define getmid(l,r) (l + (r - l) / 2)
 23 #define MP(a,b) make_pair(a,b)
 24 typedef long long ll;
 25 const int INF = 1 << 30;
 26 const int maxn = 20010;
 27 
 28 int first[maxn],next[maxn * 3],ver[maxn * 3],ecnt;
 29 int dfn[maxn],low[maxn],sc[maxn],tot,scnt,din[maxn],dout[maxn];
 30 int n,m,T;
 31 stack<int> S;
 32 
 33 void Add_edge(int u,int v){
 34     next[++ecnt] = first[u];
 35     ver[ecnt] = v;
 36     first[u] = ecnt;
 37 }
 38 
 39 void Dfs(int p){
 40     dfn[p] = low[p] = ++tot;
 41     S.push(p);
 42     for(int i = first[p]; i != -1; i = next[i]){
 43         int v = ver[i];
 44         if(!dfn[v]){
 45             Dfs(v);
 46             low[p] = min(low[p],low[v]);
 47         }
 48         else if(!sc[v]){
 49             low[p] = min(low[p],dfn[v]);
 50         }
 51     }
 52     if(low[p] == dfn[p]){
 53         ++scnt;
 54         while(1){
 55             int x = S.top();
 56             S.pop();
 57             sc[x] = scnt;
 58             if(x == p) break;
 59         }
 60     }
 61 }
 62 
 63 void Tarjan(){
 64     memset(dfn,0,sizeof(dfn));
 65     memset(low,0,sizeof(low));
 66     memset(sc,0,sizeof(sc));
 67     while(!S.empty()) S.pop();
 68     for(int i = 1; i <= n; ++i)
 69         if(!dfn[i]) Dfs(i);
 70 }
 71 
 72 void Init(){
 73     memset(first,-1,sizeof(first));
 74     memset(din,0,sizeof(din));
 75     memset(dout,0,sizeof(dout));
 76     ecnt = tot = scnt = 0;
 77 }
 78 
 79 int main(){
 80     int a,b;
 81     scanf("%d",&T);
 82     while(T--){
 83         Init();
 84         scanf("%d%d",&n,&m);
 85         for(int i = 1; i <= m; ++i){
 86             scanf("%d%d",&a,&b);
 87             Add_edge(a,b);
 88         }
 89         Tarjan();
 90         if(scnt == 1){
 91             printf("0\n");
 92             continue;
 93         }
 94         for(int k = 1; k <= n; ++k){
 95             for(int i = first[k]; i != -1; i = next[i]){
 96                 int v = ver[i];
 97                 if(sc[k] != sc[v]){
 98                     dout[sc[k]]++;
 99                     din[sc[v]]++;
100                 }
101             }
102         }
103         int c1,c2;
104         c1 = c2 = 0;
105         for(int i = 1; i <= scnt; ++i){
106             if(dout[i] == 0) ++c1;
107             if(din[i] == 0) ++c2;
108         }
109         printf("%d\n",max(c1,c2));
110     }
111     return 0;
112 }

 

转载于:https://www.cnblogs.com/naturepengchen/articles/4023569.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值