第八周作业——班长竞选

班长竞选

一、题目
  大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

二、输入
  本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

三、输出
  对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

四、样例输入输出

Input

2
4 3
3 2
2 0
2 1 
 
3 3
1 0
2 1
0 2

Output

Case 1: 2
0 1
Case 2: 2
0 1 2

五、解题思路
  Kosaraju算法。
  第一遍dfs,确定原图g1的后序序列,利用dfn数组存储。
  第二遍dfs,在反图g2中逆后序遍历节点,每次由起点遍历到的点构成一个SCC,c数据记录每个点所在的SCC编号,SCC数组记录每个SCC中点的数目。
  缩点,将点之间的连通关系转为SCC之间的连通关系,构建图g3。
  第三遍dfs,在图g3的反图g4中,遍历入度为0的点(即为g3中出度为0的点,g3利用vector存储即为g3[i].size),利用sum数组存储每一个SCC遍历得到的ans(通过当前SCC能够到达的所有SCC中所有节点的总和-1,即为支持该SCC中成员的所有人数)。
  求出SUM中的最大值,所有ans等于最大值的SCC中的节点即为答案,可存入优先级队列,之后输出。

六、代码样例

#include<iostream>
#include<queue>
#include <vector>
#include <string.h>
using namespace std;
int t;
int n,m;

int vis[5010];   
int dfn[5010];  //dfs后序列中的第i的点 
int dcnt;     //dfs计数 
int scnt;     //scc计数 
int c[5010];  //存储i的scc编号 
vector<int> g1[5010],g2[5010];  //g1原图,g2反图
vector<int> g3[5010],g4[5010]; 

int ans;
int scc[5010];
int sum[5010];

void dfs1(int x)
{
	vis[x]=1;
	for(int i=0;i<g1[x].size();i++)
	{
		int y=g1[x][i];
		if(!vis[y])
		{
     		dfs1(y);
		}
	}
	dfn[++dcnt]=x;
}

void dfs2(int x)
{
	c[x]=scnt;
	scc[scnt]++;
	for(int i=0;i<g2[x].size();i++)
	{
		int y=g2[x][i];
		if(!c[y]) dfs2(y);
	}
}

void kosaraju()
{
	dcnt=scnt=0;
    for(int i=0;i<5010;i++)
    {
    	c[i]=vis[i]=scc[i]=dfn[i]=sum[i]=0;
	}
    
	for(int i=0;i<n;i++)   //第一遍
	{
		if(!vis[i]) dfs1(i);
	}
	
	for(int i=n;i>=1;i--)  //逆后序,遍历,确定scc 
	{
		if(!c[dfn[i]])
		{
		    ++scnt; 
			dfs2(dfn[i]);	 
		}
	}
}

void shrink()   //缩点 
{
	for(int x=0;x<n;x++)
	{
		for(int i=0;i<g1[x].size();i++)
		{
			int y=g1[x][i];
			if(c[x]==c[y]) continue;
			g3[c[x]].push_back(c[y]);
			g4[c[y]].push_back(c[x]);  
		}
	}
}

void dfs(int x)  //求ans 
{
	vis[x]=1;
	ans=ans+scc[x];
	for(int i=0;i<g4[x].size();i++)
	{
		int y=g4[x][i]; 
		if(!vis[y]) dfs(y);
	}
}

int main()
{
	cin>>t;
	for(int i=0;i<t;i++)
	{
		cin>>n>>m;
		
	    for(int i=0;i<=n;i++)
	    {
	    	g1[i].clear();
	    	g2[i].clear();
	    	g3[i].clear();
	    	g4[i].clear();
		}
		
		int a,b;
		for(int i=0;i<m;i++)
		{
			cin>>a>>b;
		    g1[a].push_back(b);
			g2[b].push_back(a); 
		}
		
		kosaraju(); 
		
		shrink();
		
		int max=0;                       //求每个出度的0的scc的点数的最大值 
		for(int i=1;i<=scnt;i++)
		{
			if(g3[i].size()==0)  //出度为0 
			{
				ans=-1;
				for(int i=0;i<=scnt;i++)
                {
                 	vis[i]=0;
             	}
				dfs(i);
				sum[i]=ans;
	            if(max<ans) max=ans;
			}
		}
		
		priority_queue<int> q; 
		
		for(int i=0;i<n;i++) 
		{
			if(sum[c[i]]==max)
			{
				q.push(-i);
			}
		}
		
		cout<<"Case "<<i+1<<": "<<max<<endl;
		
		int z=0;
		while(q.size()) 
		{
			if(z!=0) cout<<" ";
			cout<<0-q.top();
			z++;
			q.pop();
		}
		cout<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值