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 }