C 班长竞选
题意描述
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
输入描述
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0
输出描述
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,行末不要有空格。
输入样例
2
4 3
3 2
2 0
2 1
3 3
1 0
2 1
0 2
输出样例
Case 1: 2
0 1
Case 2: 2
0 1 2
相关概念
- SCC(Strong Connected Component):极大的强连通子图
- DFS序分类:
(1)前序:第一次到达点x的次序
(2)后续:x点遍历完成的次序,即回溯时间
(3)逆后序:后续的逆序
- Kosaraju:用于找到图中所有的SCC
(1)第一遍DFS确定原图的逆后序序列
(2)第二遍DFS在反图中按照逆后序序列进行遍历,每次由起点遍历到的点即构成一个SCC
Kosaraju模板
#include
#include
#include
using namespace std;
const int N=5005,M=30005;
int n,c[N],dfn[N],vis[N],dcnt,scnt;
vector<int> G1[N],G2[N];
void dfs1(int x){
vis[x]=1;
for(int i=0;i<G1.size();i++){
int y=G1[i];
if(!vis[y]) dfs1(y);
}
dfn[++dcnt]=x;
}
void dfs2(int x){
c[x]=scnt;
for(int i=0;i<G2.size();i++){
int y=G2[i];
if(!c[y]) dfs(y);
}
}
void kosaraju(){
dcnt=scnt=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
if(!vis[i]) dfs1(i);
}
for(int i=n;i>=1;i--){
if(!c[dfn[i]]) ++scnt,dfs2(dfn[i]);
}
}
思路分析
- 利用Kosaraju求SCC
- 缩点
- 对于属于第 i 个 SCC 的点来说,答案分为两部分,令 SCC[i] 表示第 i 个 SCC 中点的个数
• 当前 SCC 中的点,ans += SCC[i] – 1(去除自己)
• 其它 SCC 中的点,SUM ( SCC[ j] ),其中 j 可到达 I
- 最后答案一定出现在出度为 0 的 SCC中
- 将边反向,对每个入度为 0 的点进行 dfs,计算其能到达的点的SUM(SCC[ j])
代码
#include
#include
#include
#include
#include
using namespace std;
const int N=5005,M=30005;
int n,c[N],dfn[N],vis[N],dcnt,scnt,head1[N],head2[N],head3[M],
scc[N],in_deg[N],tot1,tot2,tot3,ans[N],maxans,vis2[N],m,T,anscnt;
struct Edge{
int to,next,w;
};
set<pair<int,int>> s;
map <int,int> ansno;
Edge e1[M],e2[M],e3[M],e4[M];
void add1(int x,int y){
e1[++tot1].to=y;
e1[tot1].next=head1[x];
head1[x]=tot1;
}
void add2(int x,int y){
e2[++tot2].to=y;
e2[tot2].next=head2[x];
head2[x]=tot2;
}
void add3(int x,int y){
e3[++tot3].to=y;
e3[tot3].next=head3[x];
head3[x]=tot3;
in_deg[y]++;
}
void dfs1(int x){
vis[x]=1;
for(int i=head1[x];i;i=e1[i].next){
int y=e1[i].to;
if(!vis[y]) dfs1(y);
}
dfn[++dcnt]=x;
}
void dfs2(int x){
c[x]=scnt;
scc[scnt]++;
for(int i=head2[x];i;i=e2[i].next){
int y=e2[i].to;
if(!c[y]) dfs2(y);
}
}
void kosaraju(){
dcnt=scnt=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
if(!vis[i]) dfs1(i);
}
for(int i=n;i>=1;i--){
if(!c[dfn[i]]) ++scnt,dfs2(dfn[i]);
}
}
void dfs3(int x,int s){
vis2[x]=1;
for(int i=head3[x];i;i=e3[i].next){
int y=e3[i].to;
if(!vis2[y]){
vis2[y]=1;
ans[s]+=scc[y];
dfs3(y,s);
}
}
}
void init(){
tot1=tot2=tot3=0;
s.clear();ansno.clear();
memset(head1,0,sizeof(head1));
memset(head2,0,sizeof(head2));
memset(head3,0,sizeof(head2));
memset(scc,0,sizeof(scc));
memset(in_deg,0,sizeof(in_deg));
memset(ans,0,sizeof(ans));
maxans=0;
anscnt=0;
}
int main(){
cin>>T;
for(int z=1;z<=T;z++){
cin>>n>>m;
init();
int u,v;
while(m--){
cin>>u>>v;
add1(u,v);
add2(v,u);
}
kosaraju();
for(int i=0;i<n;i++){
for(int j=head1[i];j;j=e1[j].next){
int y=e1[j].to;
if(c[i]!=c[y]){
s.insert({c[y],c[i]});
}
}
}
for(auto &x:s){
add3(x.first,x.second);
}
for(int i=1;i<=scnt;i++){
if(in_deg[i]==0){
memset(vis2,0,sizeof(vis));
ans[i]+=scc[i]-1;
dfs3(i,i);
}
}
for(int i=1;i<=scnt;i++){
if(maxans<ans[i]) maxans=ans[i];
}
for(int i=1;i<=scnt;i++){
if(ans[i]==maxans) {
ansno[i]=1;
}
}
cout<<"Case "<<z<<": "<<maxans<<endl;
for(int i=0;i<n;i++){
if(ansno[c[i]]){
if(anscnt==0){
cout<<i;anscnt++;
}
else cout<<" "<<i;
}
}
cout<<endl;
}
}