hdu3639 强连通

题意:有 n 个人,m 组支持关系,已知支持关系可以传递,比如 A 支持 B,则所有支持 A 的人也同时支持 B,问哪些人获得的支持数最多,最多获得多少支持(自己不能获得自己的支持)。

首先,如果一些人他们互相支持,那么他们的支持数肯定都是一样的,所有支持他们其中一个人的也同时支持他们所有人,所以对于这些人我们可以强连通缩点。然后就获得了一个有向无环图,每个强连通分量有各自的人数。由于支持的人数可以传递,所以某个人支持了别人,那么他获得的支持数就一定不如他支持的人多,那么其实我们需要获得的就只是那些没有支持过别人的人,所以我们可以通过反向建图,那些没有支持别人的人就是入度为 0 的点,从每个这样的点开始DFS下去统计一共有多少个人是他能够到达的,然后再加上他自己的人数 - 1(不能获得自己的支持),这样我们再从中找出最大值以及这些人就行了。

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stack>
  4 #include<queue>
  5 using namespace std;
  6 
  7 const int maxn=5005;
  8 const int maxm=30005;
  9 
 10 int head[2][maxn],point[2][maxm],nxt[2][maxm],size[2];
 11 int n,t,scccnt,maxx;
 12 int stx[maxn],low[maxn],scc[maxn];
 13 int dp[maxn],id[maxn],vis[maxn];
 14 stack<int>S;
 15 
 16 int max(int a,int b){return a>b?a:b;}
 17 
 18 void init(){
 19     memset(head,-1,sizeof(head));
 20     size[0]=size[1]=0;
 21     memset(dp,0,sizeof(dp));
 22     memset(id,0,sizeof(id));
 23     maxx=0;
 24 }
 25 
 26 void add(int a,int b,int c=0){
 27     if(c){
 28         for(int i=head[1][a];~i;i=nxt[1][i])if(point[1][i]==b)return;
 29     }
 30     point[c][size[c]]=b;
 31     nxt[c][size[c]]=head[c][a];
 32     head[c][a]=size[c]++;
 33 }
 34 
 35 void dfs(int s){
 36     stx[s]=low[s]=++t;
 37     S.push(s);
 38     for(int i=head[0][s];~i;i=nxt[0][i]){
 39         int j=point[0][i];
 40         if(!stx[j]){
 41             dfs(j);
 42             low[s]=min(low[s],low[j]);
 43         }
 44         else if(!scc[j]){
 45             low[s]=min(low[s],stx[j]);
 46         }
 47     }
 48     if(low[s]==stx[s]){
 49         scccnt++;
 50         while(1){
 51             int u=S.top();S.pop();
 52             scc[u]=scccnt;
 53             dp[scccnt]++;
 54             if(s==u)break;
 55         }
 56     }
 57 }
 58 
 59 void setscc(){
 60     memset(stx,0,sizeof(stx));
 61     memset(scc,0,sizeof(scc));
 62     t=scccnt=0;
 63     for(int i=0;i<n;++i)if(!stx[i])dfs(i);
 64     for(int i=0;i<n;++i){
 65         for(int j=head[0][i];~j;j=nxt[0][j]){
 66             int k=point[0][j];
 67             if(scc[i]!=scc[k]){
 68                 add(scc[i],scc[k],1);
 69                 id[scc[k]]++;
 70             }
 71         }
 72     }
 73 }
 74 
 75 int Dp(int s){
 76     int ans=0;
 77     vis[s]=1;
 78     for(int i=head[1][s];~i;i=nxt[1][i]){
 79         if(!vis[point[1][i]])ans+=Dp(point[1][i]);
 80     }
 81     return ans+dp[s];
 82 }
 83 
 84 int main(){
 85     int T;
 86     scanf("%d",&T);
 87     for(int q=1;q<=T;q++){
 88         int m;
 89         scanf("%d%d",&n,&m);
 90         init();
 91         while(m--){
 92             int a,b;
 93             scanf("%d%d",&a,&b);
 94             add(b,a);
 95         }
 96         setscc();
 97         for(int i=1;i<=scccnt;++i)if(!id[i]){
 98             memset(vis,0,sizeof(vis));
 99             dp[i]=Dp(i)-1;
100             if(dp[i]>maxx)maxx=dp[i];
101         }
102         int ans=0;
103         printf("Case %d: %d\n",q,maxx);
104         int cnt=0;
105         for(int i=0;i<n;++i){
106             if(!id[scc[i]]&&dp[scc[i]]==maxx){
107                 if(cnt++)printf(" ");
108                 printf("%d",i);
109             }
110         }
111         printf("\n");
112     }
113     return 0;
114 }
View Code

 

转载于:https://www.cnblogs.com/cenariusxz/p/4813371.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值