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

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

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

本题为什么反向建图?

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

以本图为例,3,4,5分别为最优点
以本图为例,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;
}
原创文章 38 获赞 7 访问量 1757

猜你喜欢

转载自blog.csdn.net/Alanrookie/article/details/105923648