Summer Holiday HDU - 1827 Tarjian

题目链接:HDU-1827

主要思路:

先用Tarjian处理出强联通块,然后将每个点的边转为强联通块之间的边。然后连上一个个入度为0的强联通块中最小的结点即可。

正确性解释:用Tarjian算法处理出强连通块之后把每个强联通块看成是一个点,故这幅图肯定无环(若有环则早就并入一个强联通块内)。在将入度为0的点连通后就可以将所有入度不为0的点联通(每个点都有其最早的祖先,其入度一定为0)(可以自己多画几幅图理解一下)。

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 1005
using namespace std;
struct E {
	int nx,to,from;
} edge[M<<1];//注意边的数量范围是2000 
int tot=0,head[M];
void Addedge(int a,int b) {
	edge[++tot].to=b;
	edge[tot].from=a;
	edge[tot].nx=head[a];
	head[a]=tot;
}
int val[M];
int top,T,Bcnt;
int Belong[M],ID[M],low[M],stack[M],cost[M];
bool inst[M];
void Tarjian(int now) { 
	ID[now]=low[now]=++T;
	stack[++top]=now;
	inst[now]=1;
	for(int i=head[now]; i; i=edge[i].nx) {
		int nxt=edge[i].to;
		if(!ID[nxt]) {
			Tarjian(nxt);
			if(low[now]>low[nxt])low[now]=low[nxt];
		} else {
			if(inst[nxt]&&ID[nxt]<low[now])low[now]=ID[nxt];
		}
	}
	int nxt;
	if(ID[now]==low[now]) {
		Bcnt++;
		cost[Bcnt]=1e9;//初始要是很大的一个值 
		do {
			nxt=stack[top--];
			inst[nxt]=0;
			Belong[nxt]=Bcnt;
			cost[Bcnt]=min(cost[Bcnt],val[nxt]);//统计这个块内结点最小的费用 
		} while(now!=nxt);
	}
}
int in[M];
void Init(int n){
	tot=0;
	for(int i=1;i<=n;i++)head[i]=0,ID[i]=0,in[i]=0;//初始化 
}
void solve(int n,int m) {
	int ans1=0,ans2=0;
	top=T=Bcnt=0;
	for(int i=1; i<=n; i++)if(!ID[i])Tarjian(i);//如果没有遍历到就遍历 
	for(int i=1;i<=m;i++){
		if(Belong[edge[i].from]==Belong[edge[i].to])continue;//如果两个点在同一个强联通块内 
		in[Belong[edge[i].to]]++;//入度加1 
	}
	for(int i=1;i<=Bcnt;i++){
		if(!in[i])ans2+=cost[i],ans1++;//入度为0则连这个强联通块 
	}
	printf("%d %d\n",ans1,ans2);
}
int main() {
	int n,m;
	while(~scanf("%d%d",&n,&m)) {
		Init(n);//注意要初始化 
		for(int i=1; i<=n; i++)scanf("%d",&val[i]);
		for(int i=1;i<=m;i++) {
			int a,b;
			scanf("%d%d",&a,&b);
			Addedge(a,b);
		}
		solve(n,m);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81452290
今日推荐