WEEK 8 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

思路

首先使用Kosaraju算法求出强连通分量,第一次 dfs 确定原图的逆后序序列,第二次 dfs 在反图中按照逆后序序列进行遍历。
然后根据反图进行缩点,生成一个新图,该图里的每个点都是一个强连通分量。
令 SCC[i] 为第 i 个强连通分量中点的个数,对于属于第 i 个强连通分量的点来说,答案分为两部分:
当前强联通分量中的点,SCC[i] – 1(去除自己)
其它强联通分量中的点,sum(SCC[j]),其中 j 为可到达 i 的强连通分量
最后答案一定出现在出度为 0 的强联通分量中,因此对新图中每个出度为 0 的点进行 dfs,计算其能到达的点的 SUM(SCC[j]),即可得到答案。

代码

#include<iostream>
#include<vector>
#include<string.h>

using namespace std;

const int N = 5005;
const int M = 30005;

struct edge
{
	int to;
	int next;
};

int head1[N], head2[N], head3[N], dfn[N], c[N], vis[N];
int scc[N], ans[N], outdeg[N];
int dcnt, scnt;
edge e1[M], e2[M], e3[M];
int tot, tot3;
int n, m, x;

void add(int u, int v)
{
	e1[++tot].to = v;
	e1[tot].next = head1[u];
	head1[u] = tot;
	e2[tot].to = u;
	e2[tot].next = head2[v];
	head2[v] = tot;
}

void add3(int u, int v)
{
	e3[tot3].to = v;
	e3[tot3].next = head3[u];
	head3[u] = tot3;
	tot3++;
}

void dfs1(int s)
{
	vis[s] = 1;
	for (int i = head1[s]; i != -1; i = e1[i].next)
	{
		int v = e1[i].to;
		if (vis[v] == 0)
		{
			dfs1(v);
		}
	}
	dfn[++dcnt] = s;
}

void dfs2(int s)
{
	c[s] = scnt;
	scc[scnt]++;
	for (int i = head2[s]; i != -1; i = e2[i].next)
	{
		int v = e2[i].to;
		if (c[v] == 0)
			dfs2(v);
	}
}

void dfs3(int s)
{
	vis[s] = 1;
	x += scc[s];
	for (int i = head3[s]; i != -1; i = e3[i].next)
	{
		int v=e3[i].to;
		if (!vis[v])
			dfs3(v);
	}
}

void kosaraju()
{
	dcnt = scnt = 0;
	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 ini()
{
	tot3 = 0,tot = 0;
	memset(head1, -1, sizeof(head1));
	memset(head2, -1, sizeof(head2));
	memset(head3, -1, sizeof(head3));
	memset(outdeg, 0, sizeof(outdeg));
	memset(c, 0, sizeof(c));
	memset(vis, 0, sizeof(vis));
	memset(scc, 0, sizeof(scc));
	memset(ans, 0, sizeof(ans));
}

int main()
{
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++)
	{
		scanf("%d%d", &n, &m);
		ini();
		while (m--)
		{
			int a, b;
			scanf("%d%d", &a, &b);
			a++,b++;
			add(a, b);
		}
		kosaraju();
		for (int j = 1; j <= n; j++)
		{
			for (int k = head1[j]; k != -1; k = e1[k].next)
			{
				if (c[j] != c[e1[k].to])
				{
					add3(c[e1[k].to], 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 flag = true;
		for (int j = 1; j <= n; j++)
		{
			if (ans[c[j]] == max)
			{
				if (flag)
					flag = false, printf("%d", j - 1);
				else
					printf(" %d", j - 1);
			}
		}
		printf("\n");
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值