zoukankan      html  css  js  c++  java
  • 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
    */
    

      

  • 相关阅读:
    公司的OA系统基础框架系统(光标办公平台)
    通用权限控制系统--系统设计
    聘.Net软件工程师(昆明)
    对AgileFramework的思考
    iTextSharp.text.Rectangle 使用方法说明
    Castle Aspect# 难倒只支持一个拦截器?
    聘云南昆明地区的.Net工程师
    招聘云南软件销售人员
    给vncviewer 添加调用函数 GIS
    分享一个c++ 加密算法 ,在百度贴吧找的,比较好玩 GIS
  • 原文地址:https://www.cnblogs.com/chengsheng/p/4681326.html
Copyright © 2011-2022 走看看