您的位置:首页 > 其它

HDU 3639 Hawk-and-Chicken(强连通分量+缩点)

2017-07-24 21:38 316 查看


Hawk-and-Chicken

Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 45 Accepted Submission(s): 28
 
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
4000
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

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


 
Author
Dragon
 
题意:
        给你一个有向图,如果从u点能到达v点,那么说u是v的粉丝,现在要你按序输出那些粉丝数目最多的点编号.
分析:
        假设该图是一个强连通图,那么任一点都有n-1个粉丝(即n-1个点能到达它).所以我们把该图缩点变成一个新的DAG图.
把强连通子图,缩成一个点,然后进行接下来的判断。
然后我们可以采取反向建图的策略,这样支持数最大的节点只可能在入度为0的点中产生,只需要对所有入度为0的点dfs一遍计算它能到达的所有顶点数即可。但是还有一个问题,当遇到有向环时(也是一个强连通分量),dfs的过程中对于环中的最后一个点会产生错误的判断,显然这个环中的所有点状态完全一样,可以使用Tarjan的强连通分量进行缩点。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <utility>
using namespace std;
const int INF = 0x3fffffff;
const int maxn = 5005;
int n, m;
int dfs_clock, scc_cnt;
int pre[maxn], sccno[maxn], low[maxn];
int num[maxn];//每个连通分量有多少点;
int indegree[maxn];//新dag逆图的入度
int fans[maxn];//每个点的粉丝数量
int visited[maxn];
vector<int> g1[maxn], g2[maxn];
stack<int> sta;
void tarjan(int u) {
pre[u] = low[u] = ++dfs_clock;
sta.push(u);
for (int i = 0; i < g1[u].size(); ++i) {
int v = g1[u][i];
if (!pre[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (!sccno[v]){
low[u] = min(low[u], pre[v]);
}
}

if (low[u] == pre[u]) {
scc_cnt++;
num[scc_cnt] = 0;
while (1) {
int x = sta.top();
sccno[x] = scc_cnt;
num[scc_cnt]++;
sta.pop();
if (x == u)
break;
}
}
}
void find() {
dfs_clock = scc_cnt = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for (int i = 0; i < n; ++i) {
if (!pre[i])
tarjan(i);
}
}
int dfs(int u) {
int sum = 0;
visited[u] = 1;
for (int i = 0; i < g2[u].size(); ++i) {
int v = g2[u][i];
if (!visited[v]) {
sum += num[v] + dfs(v);
}
}
//cout << sum << endl;
return sum;
}
int main() {
//freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
for (int shit = 1; shit <= t; ++shit) {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
g1[i].clear();
g2[i].clear();
}

for (int i = 0; i < m; ++i) {
int a, b;
scanf("%d%d", &a, &b);
g1[a].push_back(b);
}
::find();
//逆置;
memset(indegree, 0, sizeof(indegree));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < g1[i].size(); ++j) {
int u = g1[i][j];
int v = i;
//cout << u << " " << v << endl;
//在一个强连通分量,缩点---》把一个强连通分量看成一个点
if (sccno[u] != sccno[v]) {
g2[sccno[u]].push_back(sccno[v]);
indegree[sccno[v]]++;
}
}
}
int fmax = -1;
memset(fans, 0, sizeof(fans));
for (int i = 1; i <= scc_cnt; ++i) {
if (!indegree[i]) {
memset(visited, 0, sizeof(visited));
// 在连通分量中的粉丝不包括自己
fans[i] = num[i] - 1 + dfs(i);
fmax = max(fans[i], fmax);
}
}
bool win[maxn];
for (int i = 0; i < n; ++i) {
win[i] = false;
if (fans[sccno[i]] == fmax) {
win[i] = true;
}
}
int first = 0;
printf("Case %d: %d\n", shit, fmax);
for (int i = 0; i < n; ++i) {
if (win[i]) {
if (!first) {
printf("%d",i);
first = 1;
} else {
printf(" %d",i);
}
}
}
printf("\n");
}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: