【缩点】班长竞选

问题描述

大学班级选班长,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(){
//  freopen("in.txt","r",stdin);
    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;
}

你可能感兴趣的:(#,图树,程序设计思维与实践)