您的位置:首页 > 其它

HDU 3639 Hawk-and-Chicken(强连通缩点+反向建图)

2017-05-06 17:04 465 查看
http://acm.hdu.edu.cn/showproblem.php?pid=3639

题意:

有一群孩子正在玩老鹰抓小鸡,由于想当老鹰的人不少,孩子们通过投票的方式产生,但是投票有这么一条规则:投票具有传递性,A支持B,B支持C,那么C获得2票(A.B共两票),输出最多能获得的票数是多少张和获得最多票数的人是谁?

思路:

先强连通缩点反向建图,在计算强连通的时候,需要保存每个连通分支的结点个数。

为什么要反向建图呢?因为要寻找票数最多的,那么肯定是入度为0的点,然后dfs计算它的子节点的权值(连通分支结点个数)加起来有多少。

注意,最后要减去1,不能把自己也算进去。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
using namespace std;

const int maxn=5000+5;

int n,m;
int sum;

vector<int> G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
int num[maxn];   //记录每个连通分支的结点个数
stack<int> S;

vector<int> rG[maxn];
int in[maxn];
int vis[maxn];
int cnt[maxn];

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 (pre[u] == lowlink[u])
{
scc_cnt++;
num[scc_cnt]=0;
for(;;)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
num[scc_cnt]++;
if (x == u) break;
}
}
}

void find_scc(int n)
{
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]) dfs(i);
}

void dfs2(int u)
{
vis[u]=1;
sum+=num[u];
for(int i=0;i<rG[u].size();i++)
{
int v=rG[u][i];
if(!vis[v])  dfs2(v);
}
}

int main()
{
//freopen("D:\\input.txt","r",stdin);
int T;
int kase=0;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)  G[i].clear();
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
find_scc(n);

for(int i=1;i<=scc_cnt;i++)  rG[i].clear();
memset(in,0,sizeof(in));
for(int u=0;u<n;u++)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(sccno[u]!=sccno[v])  {rG[sccno[v]].push_back(sccno[u]);in[sccno[u]]++;}
}
}

int ans=0;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=scc_cnt;i++)
{
sum=0;
if(in[i]==0)  //入度为0
{
memset(vis,0,sizeof(vis));
dfs2(i);
cnt[i]=sum-1;   //需要减1,因为它自己所处的连通分支得把自己减去
ans=max(sum-1,ans);
}
}
printf("Case %d: %d\n",++kase,ans);
bool flag=true;
for(int i=0;i<n;i++)
{
if(cnt[sccno[i]]==ans)
{
if(flag)  {printf("%d",i);flag=false;}
else      printf(" %d",i);
}
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: