Week8 作业 C - 班长竞选 HDU - 3639
题目描述:
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适,勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
输入输出约定及规模:
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格。
思路:
有向图,在一个SCC中,所有的节点都是互相支持的。
则得票最多的同学所在的SCC一定没有出边(指向其他SCC的边)。
枚举所有出度为0的SCC,如何找出每一个指向这个SCC的其他SCC?从每个点搜索,看其能否到达这个SCC?
更好的方法是,用反图,在反图中求这个SCC能达到的其他SCC,票数就是这些SCC中节点的和,然后减去1(自己给自己的票);
做法:用kosaraju求SCC、在反图中按SCC缩点、枚举入度为0的点、对每个枚举的点搜索
代码:
//求SCC时,假设当前联通块编号为i,如果搜到了编号不同的联通块j,就建立i->j的有向边,
//这样生成的是反图,因为是在反图上求SCC
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=5e3+5;
bool existEdge[MAXN][MAXN];
vector<int> G[MAXN],reG[MAXN],cG[MAXN]; //cG=contracted graph
int dfn[MAXN],dcnt; //正图dfs的后序序列 dcnt==dfs后序序列计数
int SCC[MAXN],numOfSCC,nodesOfSCC[MAXN]; //SCC[i]=k,表示节点i属于第k个联通块
int N,M;
int visited[MAXN];
int inDegree[MAXN];
int ans[MAXN];
void dfs1(int x)
{
visited[x]=1;
for(auto y:G[x])
if(!visited[y]) dfs1(y);
dfn[++dcnt]=x;
}
void dfs2(int x)
{
SCC[x]=numOfSCC;
nodesOfSCC[numOfSCC]++;
for(auto y:reG[x])
{
int u=numOfSCC,v=SCC[y];
if( v!=0 && u!=v ) //如果y属于另一个联通块,就建立联通块之间的边
{
if(existEdge[u][v]==0)
{
cG[u].push_back(v);
existEdge[u][v]=1;
inDegree[v]++; //反图中,联通块的入度
}
}
else if(v==0) dfs2(y); //如果SCC[y]=0,则y一定是当前SCC的点;否则一定是之前已经搜过的其他SCC的点,这是由Kosaraju算法本身决定的
}
}
int dfs3(int x)
{
int now=nodesOfSCC[x];
visited[x]=1;
for(auto y:cG[x])
if(!visited[y]) now+=dfs3(y);
return now;
}
void kosaraju()
{
dcnt=0,numOfSCC=0;
memset(visited,0,sizeof(visited));
//求正图的后序序列
for(int i=0;i<N;i++)
if(!visited[i]) dfs1(i);
//用逆后序序列搜反图,求联通块
for(int i=N;i>=1;i--)
if(!SCC[dfn[i]]) numOfSCC++,dfs2(dfn[i]);
}
int main()
{
// freopen("a.txt","r",stdin);
int T; cin>>T;
for(int K=1;K<=T;K++)
{
cin>>N>>M;
for(int i=0;i<=N;i++) //注意序号从0开始
G[i].clear(),reG[i].clear(),cG[i].clear();
memset(dfn,0,sizeof(dfn));
memset(SCC,0,sizeof(SCC));
memset(nodesOfSCC,0,sizeof(nodesOfSCC));
memset(visited,0,sizeof(visited));
memset(inDegree,0,sizeof(inDegree));
memset(ans,0,sizeof(ans));
memset(existEdge,0,sizeof(existEdge));
for(int i=1;i<=M;i++)
{
int u,v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
reG[v].push_back(u); //反图
}
kosaraju();
//找反图的缩图中搜入度为0的联通块
vector<int> Nodes;
for(int i=1;i<=numOfSCC;i++)
if(inDegree[i]==0) Nodes.push_back(i);
//在反图的缩图中,从入度为0的联通块开始搜
int maxi=-1;
for(auto i:Nodes)
{
memset(visited,0,sizeof(visited));
ans[i]=dfs3(i)-1;
maxi=max(maxi,ans[i]);
}
vector<int> students;
for(int i=1;i<=numOfSCC;i++)
{
if(ans[i]==maxi)
{
for(int j=0;j<N;j++)
if(SCC[j]==i) students.push_back(j);
}
}
//输出
printf("Case %d: %d\n",K,maxi);
// sort(students.begin(),students.end());
for(auto x:students)
printf( x==students[0] ? "%d" : " %d",x);
cout<<endl;
}
return 0;
}

浙公网安备 33010602011771号