问题描述
大学班级选班长,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
思路
建立有向图,则i的票数,是i可以到达的点的集合,但是若不是严格的DAG会导致将自身也算进去,故我们先对强连通分量进行缩点,可选的算法有korasaju和tarjan,这里采用tarjan,复杂度O(N+M)。建立新图,之后进行dfs求解票数,这里有一个优化,最高票数只会在入度为0的点中产生。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define mem(a,s) memset(a,s,sizeof(a))
const int N=5000+10;
const int M = 3e4 + 10;
int n,m,head[N],h[N];
int an;
struct node{int next,v;}e[M],E[M];
int dfs_num[N],tot=0,top=0,low[N],q[N];
int scnt,c[N],sum[N],ans[N];
bool vis[N];
void add_edge(int a, int b, int tt) { e[tt].next = head[a];e[tt].v = b;head[a] = tt;}
void add_EDGE(int a, int b, int tt) { E[tt].next = h[a];E[tt].v = b;h[a] = tt;}
void tarjan(int u){
dfs_num[u]=low[u]=++tot;
q[top++] = u;
vis[u] = 1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(dfs_num[v]==0){
tarjan(v);
low[u]=(low[u]<low[v]?low[u]:low[v]);
}else if(vis[v])
low[u]=(low[u]<low[v]?low[u]:low[v]);
}
if(dfs_num[u]==low[u]){
int v;
while(true){
vis[v = q[--top]]=0;
sum[c[v] = scnt]++;
if(u==v)
break;
}
scnt++;
}
}
void buildG(){
mem(low, 0);
int tt = 0;
rep(u,0,n-1){
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(c[u]==c[v])continue;
low[c[v]]++;
add_EDGE(c[u], c[v], ++tt);
}
}
}
void dfs(int u){
vis[u] = 1;
an += sum[u];
for(int i=h[u];i;i=E[i].next){
int v=E[i].v;
if(!vis[v])
dfs(v);
}
}
int main(){
int T;
scanf("%d", &T);
rep(t,1,T){
tot = top = scnt=0;
mem(head, 0);mem(h, 0);
mem(dfs_num, 0);mem(low, 0);
mem(vis, false);
mem(ans, 0);
mem(sum, 0);
scanf("%d%d", &n, &m);
rep(i,1,m){
int x, y;
scanf("%d%d",&y,&x);
add_edge(x, y, i);
}
rep(i, 0, n-1) if (!dfs_num[i]) tarjan(i);
buildG();
int maxn=0;
mem(vis, false);
rep(i,0,scnt-1)if(!low[i]){
an = 0;
dfs(i);
mem(vis,false);
ans[i] = an;
maxn = (maxn > ans[i] ? maxn : ans[i]);
}
printf("Case %d: %d\n", t, maxn-1);
bool pd = 1;
rep(i, 0, n-1)
if (ans[c[i]] == maxn){
if(pd){
printf("%d",i);
pd = 0;
}
else
printf(" %d",i);
}
printf("\n");
}
return 0;
}