week8 作业C 班长竞选

班长竞选

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

Input

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

Output

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

Sample Input

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

Sample Output

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

我的思路:
这道题是要求得票最高的票数和人,这里要考虑到的是,可能存在环,即一部分人投跟这部分中另一个人,最后达到首尾相连的情况,于是,我们可以考虑使用SCC加缩点的方法,计算票数。SCC的生成方法是使用2次dfs,分别计算逆后序,和点所属SCC。然后,进行缩点,从而得到了一个简化的图,找到出度为0的点即可能为结果的点。然后再计算这些出度为0的点的得票数,这就要使用最后一次dfs,利用缩点图的反图,找到所有可以到达这个点的其他点,并做票数统计。(注意可能结果的点不止一个,且所属SCC可能不同,记得记录)

我的总结:
这道题稍微有些复杂,用到的数组和变量比较多,一定要区分好,并做好这些变量的初始化。

我的代码:

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;

int T,N,M,a,b,ind,bf[5001],scnt;
int vis[5001],c[5001],cnt[5001],co_unt[5001],vis_end[5001];
vector<int > dis[5001],dis1[5001],G1[5001],G2[5001],ansn;
long long anss,ans1;

void dfs1(int x)
{
	vis[x]=1;
	for(int i=0;i<dis[x].size();i++)
	{
		if(!vis[dis[x][i]])
		{
			dfs1(dis[x][i]);
		}
	}
	bf[ind++]=x;
}

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

void dfs3(int x) 
{
	vis_end[x]=1;
	for(int y=0;y<G2[x].size();y++)
	{
		if(!vis_end[G2[x][y]])
		{
			ans1=ans1+cnt[G2[x][y]];
			//cout<<x<<" "<<ans1<<endl; 
			dfs3(G2[x][y]);
		}
	}
}

void init()
{	
	ind=0;
	scnt=0;
	anss=0;
	for(int i=0;i<N;i++)
	{
		bf[i]=vis[i]=c[i]=0;
	}
	for(int i=1;i<N+1;i++)
	{
		co_unt[i]=cnt[i]=vis_end[i]=0;
	}
	ansn.clear();
	for(int i=0;i<N;i++)
	{
		dis[i].clear();
		dis1[i].clear();
	} 
	for(int i=1;i<=N;i++)
	{
		G1[i].clear();
		G2[i].clear();
	}
	
}

int main()
{
	cin>>T;
	for(int q=1;q<=T;q++){
		cin>>N>>M;
		init();
		//cout<<co_unt[2]<<endl; 
		while(M--){
			cin>>a>>b;
			dis[a].push_back(b);
			dis1[b].push_back(a);
		}
		for(int i=0;i<N;i++)
			if(!vis[i]) dfs1(i);
		for(int i=N-1;i>=0;i--)
			if(!c[bf[i]]) ++scnt,dfs2(bf[i]);
		for(int i=0;i<N;i++)
		{
			for(int k=0;k<dis[i].size();k++)
			{
				if(c[i]==c[dis[i][k]]) continue;
				G1[c[i]].push_back(c[dis[i][k]]);
				G2[c[dis[i][k]]].push_back(c[i]);
				co_unt[c[i]]++;
			}
		}
		for(int i=1;i<=scnt;i++)
		{
			if(!co_unt[i])
			{
				for(int k=1;k<scnt+1;k++) vis_end[k]=0;
				ans1=0;
				ans1=ans1+cnt[i]-1;
				//cout<<i<<" "<<ans1<<endl;
				dfs3(i);
				if(ans1 > anss)
				{
					anss = ans1;
					ansn.clear();
					ansn.push_back(i);
					continue;
				}
				if(ans1 == anss)
				{
					ansn.push_back(i);
				}
			}
		}
		cout<<"Case "<<q<<": "<<anss<<endl;
		bool flag=false; 
		for(int i=0;i<N;i++)
			for(int k=0;k<ansn.size();k++) 
				if(c[i]==ansn[k])
					{
					if(flag) cout<<" ";
					else flag=true;
					cout<<i;}
		//for(int i=0;i<N;i++) cout<<i<<" "<<c[i]<<endl; 
		cout<<endl;
	}
	return 0;	
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值