hdu3836 有向图最少加多少边使强联通/缩点

题意比较抽象,说两个集合相等判定是A是B子集且B是A子集,然后给你m个集合关系,表示第一个是第二个的子集,问至少添加多少个关系使所有集合想等

把集合想象成点,就变成了:n个点m条有向边,最少加多少条边使只有一个强联通分量

思路:先tarjan缩点,然后从出度为0的点向入度为0的点连边,然后其实就是max(出度为0点数,入度为0点数)==

 1 #include<stdio.h>
 2 #include<stack>
 3 #include<vector>
 4 #include<string.h>
 5 #include<algorithm>
 6 using namespace std;
 7 stack<int>s;
 8 vector<int>G[20005];
 9 int pre[20005],lowlink[20005],sccno[20005],dfs_clock,scc_cnt;
10 int into[20005],outo[20005];
11 void dfs(int u){
12   pre[u]=lowlink[u]=++dfs_clock;
13   s.push(u);
14   for (int i=0;i<G[u].size();i++){
15     int v=G[u][i];
16     if (!pre[v]){
17       dfs(v);
18       lowlink[u]=min(lowlink[u],lowlink[v]);
19     }
20     else if (!sccno[v])
21       lowlink[u]=min(lowlink[u],pre[v]);
22   }
23   if (lowlink[u]==pre[u]){
24     scc_cnt++;
25     while (1){
26       int v=s.top(); s.pop();
27       sccno[v]=scc_cnt;
28       if (v==u) break;
29     }
30   }
31 }
32 int main()
33 {
34   int n,m,i,j,u,v,k1,k2;
35   while (~scanf("%d%d",&n,&m)){
36     while (!s.empty()) s.pop();
37     for (i=1;i<=n;i++) G[i].clear();
38     for (i=1;i<=m;i++){
39       scanf("%d%d",&u,&v);
40       G[u].push_back(v);
41     }
42     dfs_clock=scc_cnt=0;
43     memset(sccno,0,sizeof(sccno));
44     memset(pre,0,sizeof(pre));
45     for (i=1;i<=n;i++)
46       if (!pre[i]) dfs(i);
47     memset(into,0,sizeof(into));
48     memset(outo,0,sizeof(outo));
49     for (i=1;i<=n;i++)
50       for (j=0;j<G[i].size();j++)
51         if (sccno[i]!=sccno[G[i][j]]){
52           into[sccno[G[i][j]]]++;
53           outo[sccno[i]]++;
54         }
55     k1=k2=0;
56     for (i=1;i<=scc_cnt;i++){
57       if (into[i]==0) k1++;
58       if (outo[i]==0) k2++;
59     }
60 //    printf("scc%d in%d ou%d\n",scc_cnt,k1,k2);
61     if (scc_cnt==1) printf("0\n");
62     else printf("%d\n",max(k1,k2));
63   }
64   return 0;
65 }
View Code

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3836

posted on 2015-04-29 15:51  xiao_xin  阅读(308)  评论(0编辑  收藏  举报

导航