Week 8——班长竞选

班长竞选

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

本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0

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

问题分析

强联通分量

强连通:有向图 G 中任意两个结点连通
强连通分量(SCC):极大的强连通子图
Kosaraju算法求强联通分量:
第一遍 dfs 确定原图的逆后序序列。
第二遍 dfs 在反图中按照逆后序序列进行遍历 ,每次由起点遍历到的点即构成一个 SCC。

解题思路

根据输入的边生成图G1和反图G2,使用Kosaraju算法求出强联通分量。

缩点,遍历所有顶点,对于点u,遍历他的所有边(u,v),如果u,v不再同一个强联通分量,则u所在的强联通分量和v所在的强联通分量间存在一条边。

缩点后,不难发现对于属于第 i 个 SCC 的点来说,答案分为 两部分,令 SCC[i] 表示第 i 个 SCC 中点的个数 (在Kosaraju算法第二次dfs遍历时求出)
当前 SCC 中的点,ans += SCC[i] – 1(去除自己)
其它 SCC 中的点 SUM ( SCC[j] ),其中 j 可到达 i

最后答案一定出现在出度为 0 的 SCC 中, 因此对每个出度为 0 的点进行 dfs,计算其能到达的点的 SUM(SCC[j]),即可得到答案。

代码分析

#include
#include
#include
#include
#define N 5010
using namespace std;

struct edge
{
	int v;
	int net;
};

int head[N],head2[N],head3[N],dfn[N],c[N],vis[N]; 
int scc[N],ans[N],outdeg[N];
int dcnt,scnt;
edge e[30050],e2[30050],e3[30050];
int index,index2,index3;
int n,m,x;

void add(int u, int v)
{
	e[index].v = v;
	e[index].net = head[u];
	head[u] = index;
	index++;
}

void add2(int u, int v)
{
	e2[index2].v = v;
	e2[index2].net = head2[u];
	head2[u] = index2;
	index2++;
}

void add3(int u, int v)
{
	e3[index3].v = v;
	e3[index3].net = head3[u];
	head3[u] = index3;
	index3++;
}

void dfs1(int s)
{
	vis[s] = 1;
	for(int i=head[s]; i!=-1; i=e[i].net)
	{
		int v=e[i].v;
		if(vis[v]==0)
		{
			dfs1(v);
		}
	}
	dfn[++dcnt] = s;//dfs后序列中第dcnt个点为s 
}

void dfs2(int s)
{
	c[s] = scnt;//s在scnt号联通分量 
	scc[scnt]++;
	for(int i=head2[s]; i!=-1; i=e2[i].net)
	{
		int v=e2[i].v;
		if(c[v]==0)
		    dfs2(v);
	}
}

void kosaraju()
{
	dcnt=scnt=0;
	memset(c,0,sizeof(c));
	memset(vis,0,sizeof(vis));
	memset(scc,0,sizeof(scc));

	for(int i=1; i<=n; i++)
	    if(vis[i]==0)
	        dfs1(i);
	for(int i=n; i>=1; i--)
	{
		if(c[dfn[i]]==0)
	    {
	    	++scnt;
	    	dfs2(dfn[i]);
		}
	}
	    
}

void dfs3(int s)//计算得票数 
{
	vis[s]=1;
	x += scc[s];
	for(int i=head3[s];i!=-1;i=e3[i].net)
	{
		if(!vis[e3[i].v])
			dfs3(e3[i].v);
	}
}


int main()
{
	int t=0;
	scanf("%d",&t);
	for(int i=1;i<=t; i++)
	{
		scanf("%d%d",&n,&m);
		memset(head,-1,sizeof(head));
		memset(head2,-1,sizeof(head2));
		memset(head3,-1,sizeof(head3));
		memset(outdeg,0,sizeof(outdeg));
		memset(ans,0,sizeof(ans));
		index3= 0;
		index2= 0;
		index = 0;	
			
		while(m--)
		{
			int p1,p2;
			scanf("%d%d",&p1,&p2);
			p1++;
			p2++;
			add(p1,p2);
			add2(p2,p1);
		}
		
		kosaraju();
		
		//缩点
		for(int j=1; j<=n; j++)
		{
			for(int k=head[j]; k!=-1; k=e[k].net) 
			{
				if(c[j]!=c[e[k].v])
				{
					add3(c[e[k].v],c[j]);
					outdeg[c[j]]++;//出度 
				}
			}
		}
		
		int max=0;
		for(int j=1; j<=scnt; j++)
		{
		    if(outdeg[j] == 0)
		    {
		    	x=0;
		    	memset(vis,0,sizeof(vis));
		    	dfs3(j);
		    	ans[j] = x;
		    	if(x > max)
		    	    max = x;
			}
		}
		printf("Case %d: %d\n",i,max-1);
		bool first=true;
		for(int j=1; j<=n; j++)
		{
			if(ans[c[j]] == max)
			{
				if(first)
				    first = false, printf("%d",j-1);
				else
				    printf(" %d",j-1);
			}
		}
		printf("\n");
	}
 } 

你可能感兴趣的:(Week 8——班长竞选)