0
点赞
收藏
分享

微信扫一扫

Hawk-and-Chicken HDU - 3639(tarjan,重点说一下为什么要反向建图)


题意:大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

这题没啥好说的,就是用tarjan把强连通分量缩点后寻找出度为零点点输出即可(当然这只是咱的第一反应),但实际操作时我们要采用反向建图。扒拉了很多博客神奇的发现竟然没几个讲为什么要反向建图的,啧啧啧,自己想了很长时间,后来竟发现愿意如此简单,咳咳,下面是重点。

本题为什么反向建图?

严格说来,正向建图也没毛病,但是正向建图不利于我们搜索计算这个点的票数是否是最高的(如果是就先记录下来最后一起输出),而反向建图更有利于搜索解题。光说估计大家也不太明白,看一下下面这个图,依此为例。(建议先阅读代码了解解题思路后再看这一部分)


Hawk-and-Chicken HDU - 3639(tarjan,重点说一下为什么要反向建图)_i++

以本图为例,3,4,5,分别为最优点,但是我们需要计算出3,4,5的票数才能判断出他们是最优点

好的,我们直接看反向建图,对于反向建图,我们很明显可以看出入度为零的点可能是答案,这是我们只需要一个dfs就可以顺着这几个点求出这几个点的票数(详见代码的dfs部分),而如果是正向建图我们就会发现出现了问题,无论论怎么搜,好像都无法一下搜到那几个点(都会有分叉,而这样分叉后再搜加上的票数就不是这个点的票数了)。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn=5010;
const int N = 5010;
int low[N],dfn[N],ans,tot;
int vis[N],belong[N],cnt;
int dp[N],num[N],in[N],sum;
int n,m;
stack<int>p;
vector<int>g[N],mp[N];
void init(){
tot=ans=0;
memset(in,0,sizeof(in));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(belong,0,sizeof(belong));
memset(num,0,sizeof(num));
for(int i=0;i<=n;i++)
g[i].clear(),mp[i].clear();
while(!p.empty()) p.pop();
}
void tarjan(int x){
low[x]=dfn[x]=++tot;
vis[x]=1;
p.push(x);
for(int i=0;i<g[x].size();i++){
int v=g[x][i];
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(vis[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]){
int v;
belong[x]=++ans;
do{
v=p.top();
p.pop();
vis[v]=0;
belong[v]=ans;//记录每个点属于的强联通块的标号
num[ans]++; //记录每个强联通块的大小
}while(v!=x);
}
}
void dfs(int x) {
vis[x]=1;
sum+=num[x];
for(int i=0; i<mp[x].size(); i++) {
int v=mp[x][i];
if(!vis[v]) dfs(v);
}
}
int main() {
int T,cas=0;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
init();
for(int i=0; i<m; i++) {
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
}
for(int i=0; i<n; i++)if(!dfn[i]) tarjan(i);
for(int i=0; i<n; i++)
for(int j=0; j<g[i].size(); j++) {
int v=g[i][j];
int x=belong[i],y=belong[v];
if(x!=y) {
mp[y].push_back(x);
in[x]++;
}
}
memset(dp,0,sizeof(dp));
int maxt=0;
for(int i=1; i<=ans; i++) {
if(!in[i]) {
memset(vis,0,sizeof(vis));
sum=0;
dfs(i);
dp[i]=sum;
if(sum>maxt) maxt=sum;
}
}
printf("Case %d: %d\n",++cas,maxt-1);
int first=1;
for(int i=0; i<n; i++) {
if(dp[belong[i]]==maxt) {
if(first) printf("%d",i);
else printf(" %d",i);
first=0;
}
}
puts("");
}
return 0;
}

 

 

举报

相关推荐

0 条评论