BNU 20860——Forwarding Emails——————【强连通图缩点+记忆化搜索】

Forwarding Emails

Time Limit: 1000ms
Memory Limit: 131072KB
This problem will be judged on UVA. Original ID: 12442
64-bit integer IO format: %lld      Java class name: Main
Type: 
None
 
 

[PDF Link]

J

Forwarding Emails

"... so forward this to ten other people, to prove that you believe the emperor has new clothes."

Aren't those sorts of emails annoying?

Martians get those sorts of emails too, but they have an innovative way of dealing with them. Instead of just forwarding them willy-nilly, or not at all, they each pick one other person they know to email those things to every time - exactly one, no less, no more (and never themselves). Now, the Martian clan chieftain wants to get an email to start going around, but he stubbornly only wants to send one email. Being the chieftain, he managed to find out who forwards emails to whom, and he wants to know: which Martian should he send it to maximize the number of Martians that see it?

Input

The first line of input will contain T (≤ 20) denoting the number of cases.

Each case starts with a line containing an integer N (2 ≤ N ≤ 50000) denoting the number of Martians in the community. Each of the next N lines contains two integers: u v (1 ≤ u, v ≤ N, u ≠ v) meaning that Martian u forwards email to Martian v.

Output

For each case, print the case number and an integer m, where m is the Martian that the chieftain should send the initial email to. If there is more than one correct answer, output the smallest number.

Sample Input

Output for Sample Input

3
3
1 2
2 3
3 1
4
1 2
2 1
4 3
3 2
5
1 2
2 1
5 3
3 4
4 5
Case 1: 1
Case 2: 4
Case 3: 3

题目大意:现在要转发邮件,有n个人,有n条边,表示u--->v可以转发邮件。有一个人想要在这n个人中选一个人作为起点进行邮件转发。想要让转发给的人尽量多,问从哪里开始转发可以满足条件。

解题思路:想转发给的人尽量多,即要求让经过的点尽量多。我们可以在有向图上进行强连通缩点。然后在缩点形成的DAG上进行DFS。然后遍历判断从哪个连通块出发能经过最多的点,在求出该连通块的最小顶点编号即为结果。

#include<bits/stdc++.h>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn=50050;
vector<int>G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
int dp[maxn],d[maxn];
stack<int>S;
set<int>ST[maxn];//新的DAG需要去重边
//tarjan 模板
void dfs(int u){
    pre[u]=lowlink[u]=++dfs_clock;
    S.push(u);
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!pre[v]){
            dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }else if(!sccno[v]){
            lowlink[u]=min(lowlink[u],pre[v]);
        }
    }
    if(lowlink[u]==pre[u]){
        scc_cnt++;
        for(;;){
            int x=S.top();S.pop();
            sccno[x]=scc_cnt;
            if(x==u)
                break;
        }
    }
}
void find_scc(int n){
    dfs_clock=scc_cnt=0;
    while(!S.empty())
        S.pop();
    memset(sccno,0,sizeof(sccno));
    memset(pre,0,sizeof(pre));
    for(int i=0;i<n;i++){
        if(!pre[i])
            dfs(i);
    }
}
int DFS(int u){
    if(dp[u])
        return dp[u];
    dp[u]=d[u];
    for(set<int>::iterator it=ST[u].begin();it!=ST[u].end();it++){
        int v=*it;
        if(!dp[v]){
            dp[u]+=DFS(v);
        }else {
            dp[u]+=dp[v];
        }
    }
    return dp[u];
}
int main(){
    int t,n,a,b,cnt=0;
    scanf("%d",&t);
    while(t--){
        memset(dp,0,sizeof(dp));
        memset(d,0,sizeof(d));
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d%d",&a,&b);
            a--,b--;
            G[a].push_back(b);
        }
        find_scc(n);
        for(int i=0;i<n;i++){
            d[sccno[i]]++;
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<G[i].size();j++){
                int v=G[i][j];
                if(sccno[i]!=sccno[v])//SB了,开始忘了加这个条件,要是不同的连通块
                    ST[sccno[i]].insert(sccno[v]);
            }
        }
        for(int i=1;i<=scc_cnt;i++){//在连通块形成的DAG上记忆化搜索
            if(!dp[i]){
                DFS(i);
            }
        }
        int sum=0,ck=0;
        for(int i=1;i<=scc_cnt;i++){
            if(dp[i]>sum){
                ck=i;
                sum=dp[i];
            }
        }
        int ok=0;
        for(int i=0;i<n;i++){
            if(sccno[i]==ck){
                ok=i;
                break;
            }
        }
        printf("Case %d: %d
",++cnt,ok+1);
        for(int i=0;i<=n;i++)
            G[i].clear();
        for(int i=1;i<=scc_cnt;i++){
            ST[i].clear();
        }
    }
    return 0;
}


/*
55
4
1 2
2 1
4 3
3 2


5
1 2
2 1
5 3
3 4
4 5
*/

  

原文地址:https://www.cnblogs.com/chengsheng/p/4681326.html