程序设计思维与实践 Week8 作业 C 班长竞选

本文介绍了一种解决大学班级选举班长问题的算法,通过Kosaraju算法求解强连通分量,缩点处理并统计最高票数及候选人。算法首先收集同学之间的推荐意见,然后利用图论中的强连通分量概念,最终找到得票最多的候选人。
摘要由CSDN通过智能技术生成

题目描述:

大学班级选班长,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开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

思路:

如果某点在环中,则该点受到的投票为环中的点数-1。

先用Kosaraju求出强连通分量,然后缩点:两边dfs,第一遍求出逆后序序列,第二遍根据逆后序序列找出所有强连通分量。

稍加思考,可以发现最后答案一定出现在出度为 0 的 SCC 中。因此遍历出度为0的点,统计其所能到达的所有的点。

技巧:1.缩点之后两个点A和B之间会有很多条边,这里只建立一条边,防止时间复杂度过高。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=5050;
int n,m,maxx,scnt,cont;
int tot,totp,totpp;
int head[maxn],headp[maxn],headpp[maxn];
int ans[maxn],du[maxn],scc[maxn],c[maxn],dfn[maxn];
bool flag[maxn],flag_scc[maxn],flag_ans[maxn],if_have_edge[maxn][maxn];
struct node
{
	int v;
	int next;
}e[30050],ep[30050],epp[30050];
void add_edge(int u,int v)//原图 
{
	e[++tot].v=v;
	e[tot].next=head[u];
	head[u]=tot;
}
void add_edge_p(int u,int v)//反图 
{
	ep[++totp].v=v;
	ep[totp].next=headp[u];
	headp[u]=totp;
}
void add_edge_pp(int u,int v)//缩点后的反向图 
{
	epp[++totpp].v=v;
	epp[totpp].next=headpp[u];
	headpp[u]=totpp;
}
void dfs(int u)
{
	flag[u]=1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(!flag[v])
		dfs(v);
	}
	dfn[++cont]=u;
}
void dfs2(int u)
{
	c[u]=scnt;scc[scnt]++; 
	for(int i=headp[u];i;i=ep[i].next)
	{
		int v=ep[i].v;
		if(!c[v])
		dfs2(v);
	}
}
void dfs3(int u,int s)
{
	flag_scc[u]=1;
	for(int i=headpp[u];i;i=epp[i].next)
	{
		int v=epp[i].v;
		if(!flag_scc[v])
		{
			flag_scc[v]=1;
			ans[s]+=scc[v];
			dfs3(v,s);
		}
	}
}
int init()
{
	int f=1,pp=0;char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){pp=pp*10+c-'0';c=getchar();}
	return f*pp;
}
void ini()
{
	memset(head,0,sizeof(head));
	memset(headp,0,sizeof(headp));
	memset(headpp,0,sizeof(headpp));
	memset(dfn,0,sizeof(dfn));
	memset(scc,0,sizeof(scc));
	memset(du,0,sizeof(du));
	memset(c,0,sizeof(c));
	memset(ans,0,sizeof(ans));
	memset(flag,0,sizeof(flag));
	memset(if_have_edge,0,sizeof(if_have_edge));
	memset(flag_ans,0,sizeof(flag_ans));
	tot=0;totp=0;totpp=0;cont=0;scnt=0;maxx=0;
}
int main()
{
	int T,x,y;
	T=init();
	for(int k=1;k<=T;k++)
	{
		ini();
		n=init();m=init();
		for(int i=1;i<=m;i++)
		{
			x=init();y=init();
			add_edge(x+1,y+1);
			add_edge_p(y+1,x+1);
		}
		for(int i=1;i<=n;i++)
		if(!flag[i]) dfs(i);
		for(int i=n;i>=1;i--)
		if(!c[dfn[i]])
		{
			scnt++;
			dfs2(dfn[i]);
		}
		for(int i=1;i<=n;i++)
		for(int j=head[i];j;j=e[j].next)
		{
			int v=e[j].v;
			if(c[i]!=c[v]&&!if_have_edge[c[v]][c[i]])
			{
				if_have_edge[c[v]][c[i]]=1;
				add_edge_pp(c[v],c[i]);
				du[c[i]]++;
			}
		}
		int maxx=-1;
		for(int i=1;i<=scnt;i++)
		if(du[i]==0)
		{
			memset(flag_scc,0,sizeof(flag_scc));			
			ans[i]+=scc[i]-1;
			dfs3(i,i);
			maxx=max(maxx,ans[i]);
		}
		for(int i=1;i<=scnt;i++)
		if(ans[i]==maxx)
		flag_ans[i]=1;
		cout<<"Case "<<k<<": "<<maxx<<endl;
		bool flag_tmp=0; 
		for(int i=1;i<=n;i++)
		if(flag_ans[c[i]])
		{
			if(!flag_tmp)
			{
				cout<<i-1;
				flag_tmp=1;
			}
			else cout<<" "<<i-1;
		}
		cout<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值