题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3639
借鉴了一位大佬的博客:https://blog.csdn.net/u013480600/article/details/32140501
题意:给你一个有向图,如果从u点能到达v点,那么说u是v的粉丝,现在要你按序输出那些粉丝数目最多的点编号.
分析:
假设该图是一个强连通图,那么任一点都有n-1个粉丝(即n-1个点能到达它).所以我们把该图缩点变成一个新的DAG图.
结论:原图中具有最多粉丝的点一定在新图的那些出度为0的点所代表的分量中.
证明:假设u节点粉丝最多且它所属的分量出度不为0,那么u节点一定是某个节点v的粉丝,所以v的粉丝必然包含了u的所有粉丝加上u本身.所以v的粉丝必然多余u.由此矛盾.
下面的问题是如何找新DAG图的每个节点(所代表分量中的原节点)的最大粉丝数? 该粉丝数=本连通分量的点数-1+本连通分量能通过ß这种边逆向走到的所有分量的点数和. 所以我们直接建立缩点树的逆图DAG即可,如果u->v表示u分量将获得v分量的所有节点作为粉丝.所以我们只需要对那几个入度为0的点做DFS即可.
每次DFS到一个新节点,该点所代表的分量节点数就都加到sum上去,表示新加了很多粉丝.最后找最大粉丝值的分量点输出即可.
特殊案例:
1
6 6
0 1
0 2
1 5
1 3
2 3
2 4
输出是
3 3
该案例的反向图:
切记不要把0号点的粉丝数算了两遍,进行dfs的时候要进行标记,一开始一直wrong在这里
代码写得有点累赘:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <map>
#include <stack>
#include <queue>
using namespace std;
const int maxn =5000+10;
int n,m,T;
int pre[maxn],low[maxn],sccno[maxn],dfs_clock,scc_cnt,w[maxn],outdeg[maxn],ans,d[maxn],flag[maxn],vis[maxn];
stack<int>S;
vector<int>G[maxn],mp[maxn];
void dfs(int u)
{
pre[u] = low[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);
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++;
int cnt =0;
for(; ;)
{
cnt++;
int x = S.top();
S.pop();
sccno[x] = scc_cnt;
if(x==u)
break;
}
w[scc_cnt]=cnt;
}
}
void find_scc()
{
dfs_clock = scc_cnt =0;
memset(sccno,0,sizeof(sccno));
memset(outdeg,0,sizeof(outdeg));
memset(d,0,sizeof(d));
memset(flag,0,sizeof(flag));
memset(w,0,sizeof(w));
memset(pre,0,sizeof(pre));
for(int i=0; i<n; i++)
if(!pre[i])
dfs(i);
}
int dfs2(int u)
{
vis[u]=1;
d[u]=w[u];
for(int i=0; i<mp[u].size(); i++)
{
int v=mp[u][i];
if(!vis[v])
d[u]+=dfs2(v);
}
return d[u];
}
void build()
{
for(int i=1; i<=scc_cnt; i++)
mp[i].clear();
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])
{
outdeg[sccno[u]]++;
mp[sccno[v]].push_back(sccno[u]);//逆向建图
}
}
}
}
int main()
{
int kase=0;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=0; i<n; i++)
G[i].clear();
int u,v;
for(int i=0; i<m; i++)
{
scanf("%d %d",&u,&v);
G[u].push_back(v);
}
find_scc();//缩点
build();//建图
vector<int>O_deg;//保存出度为0的点
for(int i=1; i<=scc_cnt; i++)
{
if(outdeg[i]==0)
{
O_deg.push_back(i);
}
}
int max_sum=-1;
for(int i=0; i<O_deg.size(); i++)//
{
memset(vis,0,sizeof(vis));//避免重复计算
max_sum=max(max_sum,dfs2(O_deg[i]));
}
for(int i=1; i<=scc_cnt; i++)
{
if(d[i]==max_sum)
flag[i]=1;
}
vector<int>s;
for(int i=0; i<n; i++)
{
if(flag[sccno[i]])
s.push_back(i);
}
ans=max_sum-1;
printf("Case %d: %d\n",++kase,ans);
for(int i=0; i<s.size(); i++)
{
if(i!=0)
printf(" %d",s[i]);
else
printf("%d",s[i]);
}
printf("\n");
}
return 0;
}