HDU 3639 Tarjan + 缩点 + 反建图 + 搜索

来源:互联网 发布:如何在淘宝上卖鱼 资质 编辑:程序博客网 时间:2024/05/17 22:38

Hawk-and-Chicken

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2911    Accepted Submission(s): 899


Problem Description
Kids in kindergarten enjoy playing a game called Hawk-and-Chicken. But there always exists a big problem: every kid in this game want to play the role of Hawk. 
So the teacher came up with an idea: Vote. Every child have some nice handkerchiefs, and if he/she think someone is suitable for the role of Hawk, he/she gives a handkerchief to this kid, which means this kid who is given the handkerchief win the support. Note the support can be transmitted. Kids who get the most supports win in the vote and able to play the role of Hawk.(A note:if A can win
support from B(A != B) A can win only one support from B in any case the number of the supports transmitted from B to A are many. And A can't win the support from himself in any case.
If two or more kids own the same number of support from others, we treat all of them as winner.
Here's a sample: 3 kids A, B and C, A gives a handkerchief to B, B gives a handkerchief to C, so C wins 2 supports and he is choosen to be the Hawk.
 

Input
There are several test cases. First is a integer T(T <= 50), means the number of test cases.
Each test case start with two integer n, m in a line (2 <= n <= 5000, 0 <m <= 30000). n means there are n children(numbered from 0 to n - 1). Each of the following m lines contains two integers A and B(A != B) denoting that the child numbered A give a handkerchief to B.
 

Output
For each test case, the output should first contain one line with "Case x:", here x means the case number start from 1. Followed by one number which is the total supports the winner(s) get. 
Then follow a line contain all the Hawks' number. The numbers must be listed in increasing order and separated by single spaces.
 

Sample Input
24 33 22 02 13 31 02 10 2
 

Sample Output
Case 1: 20 1Case 2: 20 1 2
 


感觉这道题让本菜鸡学到了很多......

题意:给你个有向图,n个点,m条边,叫你求出哪一个甚至哪一些点是其他最多点可以到达的,还求出可以到达这个点的其他点的总个数。


开始我想到了求强连通分支然后缩点,然后对于每一个点进行搜索,但复杂度太高。后来看了题解后发现需要反建图,然后对入度为0的点进行比较即可,但原理想了一下午都没想通。

后来通过在纸上模拟,才发现我一直不能理解其原理的原因是因为我把每个点进行孤立地考虑,而没有深入地思考他们之间的关系。

首先要明确一个知识点:
对于一个强连通图,假设点的个数是n,那么对于其中任意一个点,能够到达它的点的个数一定是(n-1)。(不包括他自己。)

假设我们把这么一个强连通图缩成一个点A 。现在有另外两个点B,C,他们的关系是:
B -> A -> C。 

那么对于B:能够到达他的点的个数为0。
对于C:能够到达他的点的个数为 n+1。
所以哪怕A中包含的点非常多,但当A内部任意有一个点指向另一个点C时,最优点就只能是C,而跟A中所有的点都无关。

总结:当一个点出度为大于0时,这个点一定不是此题的最优点。

那么你可能会想,为什么不直接找出度为0的点比较,而要“多此一举”地反建图再比较入度为0的点呢?

理由:方便对每个点进行搜索。对于这种稀疏图,我们大多用邻接表进行存图。而对于每一个点,我们存的是这个点指向另外的点,而不是这个点被哪些点所指向,所以反建图,有利于我们的搜索。

然后就是最终答案的搜索,目前网上题解大多是用的dfs,而我个人比较喜欢bfs,所以两种写法都写了一下。



DFS:
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int INF = 1e8;const int maxn = 5010;vector<int>    G1[maxn],G2[maxn];int low[maxn],dfn[maxn],in[maxn];int vis[maxn],instack[maxn],point[maxn],ans[maxn],dp[maxn]; int n,tot,num,sum;stack<int> S;void init(void){    tot = num = 0;    for(int i=0 ;i<=n ;i++){        G1[i].clear(),G2[i].clear();        low[i] = dfn[i] = in[i] = ans[i] = 0;        vis[i] = instack[i] = point[i] = 0;        dp[i] = -1;        }    while(S.size())    S.pop();}void Tarjan(int x){    low[x] = dfn[x] = tot++;    vis[x] = instack[x] = 1;    S.push(x);    for(int i=0 ;i<G1[x].size();i++){        int v = G1[x][i];        if(!vis[v]){            Tarjan(v);            low[x] = min(low[x],low[v]);        }        else if(instack[v]){            low[x] = min(low[x],dfn[v]);        }    }    if(low[x] == dfn[x]){        int cnt = 0;        while(1){            int t = S.top();            S.pop();            instack[t] = 0;            cnt++;            point[t] = num;             if(t == x)    break;        }        ans[num] = cnt;        num++;    }}void dfs(int x){    vis[x] = 1;    sum += ans[x];    for(int i=0 ;i<G2[x].size() ;i++){        if(!vis[G2[x][i]])            dfs(G2[x][i]);    }}int main(){    int T,_=1;    scanf("%d",&T);    while(T--){        int m;        scanf("%d%d",&n,&m);        init();        while(m--){            int x,y;            scanf("%d%d",&x,&y);            G1[x].push_back(y);        }            for(int i=0 ;i<n ;i++){            if(!vis[i]){                Tarjan(i);            }        }        for(int i=0 ;i<n ;i++){            for(int j=0;j<G1[i].size();j++){                if(point[i] != point[G1[i][j]]){                    in[point[i]]++;                    G2[point[G1[i][j]]].push_back(point[i]);                }            }        }        int res = -1;        for(int i=0 ;i<num ;i++){            if(!in[i]){                sum = 0;                memset(vis,0,sizeof(vis));                dfs(i);                dp[i] = sum;                if(sum > res){                    res = sum;                }            }        }        bool flag = 1;        printf("Case %d: %d\n",_++,res-1);        for(int i=0 ;i<n ;i++){            if(dp[point[i]] == res){                if(flag)    printf("%d",i);                else        printf(" %d",i);                flag = 0;            }        }        printf("\n");    }    return 0;} 



BFS:

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int INF = 1e8;const int maxn = 5010;vector<int>    G1[maxn],G2[maxn];int low[maxn],dfn[maxn],in[maxn];int vis[maxn],instack[maxn],point[maxn],ans[maxn],dp[maxn]; int n,tot,num,sum;stack<int> S;void init(void){    tot = num = 0;    for(int i=0 ;i<=n ;i++){        G1[i].clear(),G2[i].clear();        low[i] = dfn[i] = in[i] = ans[i] = 0;        vis[i] = instack[i] = point[i] = 0;        dp[i] = -1;        }    while(S.size())    S.pop();}void Tarjan(int x){    low[x] = dfn[x] = tot++;    vis[x] = instack[x] = 1;    S.push(x);    for(int i=0 ;i<G1[x].size();i++){        int v = G1[x][i];        if(!vis[v]){            Tarjan(v);            low[x] = min(low[x],low[v]);        }        else if(instack[v]){            low[x] = min(low[x],dfn[v]);        }    }    if(low[x] == dfn[x]){        int cnt = 0;        while(1){            int t = S.top();            S.pop();            instack[t] = 0;            cnt++;            point[t] = num;             if(t == x)    break;        }        ans[num] = cnt;        num++;    }}void bfs(int x){    queue<int> que;    vis[x] = 1;    que.push(x);    while(que.size()){        int v = que.front();        que.pop();        sum += ans[v];        for(int i=0 ;i<G2[v].size() ;i++){            if(!vis[G2[v][i]]){                vis[G2[v][i]] = 1;                que.push(G2[v][i]);            }        }    }}int main(){    int T,_=1;    scanf("%d",&T);    while(T--){        int m;        scanf("%d%d",&n,&m);        init();        while(m--){            int x,y;            scanf("%d%d",&x,&y);            G1[x].push_back(y);        }            for(int i=0 ;i<n ;i++){            if(!vis[i]){                Tarjan(i);            }        }        for(int i=0 ;i<n ;i++){            for(int j=0;j<G1[i].size();j++){                if(point[i] != point[G1[i][j]]){                    in[point[i]]++;                    G2[point[G1[i][j]]].push_back(point[i]);                }            }        }        int res = -1;        for(int i=0 ;i<num ;i++){            if(!in[i]){                sum = 0;                memset(vis,0,sizeof(vis));                bfs(i);                dp[i] = sum;                if(sum > res){                    res = sum;                }            }        }        bool flag = 1;        printf("Case %d: %d\n",_++,res-1);        for(int i=0 ;i<n ;i++){            if(dp[point[i]] == res){                if(flag)    printf("%d",i);                else        printf(" %d",i);                flag = 0;            }        }        printf("\n");    }    return 0;} 





0 0
原创粉丝点击