Tarjan

学习:Tarjan

例题:CF427 C.CheckPosts

Input 1:

3
1 2 3
3
1 2
2 3
3 2

Output 1

3 1

Input 2:

5
2 8 0 6 0
6
1 4
1 3
2 4
3 4
4 5
5 1

Output 2:

8 2

Input 3:

10
1 3 2 2 1 3 1 4 10 10
12
1 2
2 3
3 1
3 4
4 5
5 6
5 7
6 4
7 3
8 9
9 10
10 9

Output 3:

15 6

Input 4:

2
7 91
2
1 2
2 1

Output 4:

7 1

题意:有n个城市,在不同的城市建立检查站都有一定的花费。对于每一个检查站,作用范围是当前检查站所在的连通分量的所有城市,问最少需要多少钱建立检查站就可以覆盖所有城市,且对应的有多少种建立方法。

思路: 依照题意,只要在每个联通分量内最小花费的城市处建立一个检查站,则所有的检查站建立的花费和即是最小的,每个连通分量内最小的花费如果有多个,则在其中任意一个地方建立检查站都是可行的,因此对于第二问,只需要用乘法法则将每个连通分量内与最小花费相等的城市数量相乘即为答案。用一个数组记录每个连通分量的最小花费是多少,用一个vector记录连通分量内的城市坐标,再Tarjan算法结束后遍历所有连通分量,再遍历此连通分量对应的vector,计算出和最小花费相等的城市数量。

AC代码:

#include<set>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
typedef long long LL;
#define PII pair<int, int>
#define pb push_back
const int maxn = 1e5 + 5;
const LL mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
using namespace std;

int n, m;
LL MinAllCost, Method = 1;
vector<int> Tcnt[maxn];
LL TMinCost[maxn];//联通分量内最小花费point
int S[maxn];//栈
int dfn[maxn], low[maxn];//
int sig, Top = -1, cnt;//连通分量标号,栈顶,dfn
int vis[maxn];
LL cost[maxn];
vector<int> G[maxn];

void Tarjan(int x){
	vis[x] = 1;
	low[x] = dfn[x] = ++cnt;
	S[++Top] = x;
	int len = G[x].size();
	for(int i = 0; i < len; ++i){
		int v = G[x][i];
		if(vis[v] == 0) Tarjan(v);
		if(vis[v] == 1) low[x] = min(low[x], low[v]);
	}
	if(dfn[x] == low[x]){
		sig++;
		//printf("when sig++ : x = %d\n", x);
		while(1){
			low[S[Top]] = sig;
			vis[S[Top]] = -1;
			Tcnt[sig].pb(S[Top]);//连通分量内所有点
			//printf("%d ", S[Top]);
			TMinCost[sig] = min(TMinCost[sig], cost[S[Top]]);
			if(S[Top--] == x) break;
		}
		//printf("\n");
	}
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){
		scanf(" %lld", cost + i);
	}
	scanf("%d", &m);
	for(int i = 1; i <= m;++i){
		int u, v;
		scanf("%d %d", &u, &v);
		G[u].pb(v);
	}
	memset(TMinCost, inf, sizeof TMinCost);
	for(int i = 1; i <= n; ++i) if(vis[i] == 0) Tarjan(i);
	//printf("Sig = %d\n", sig);
	for(int i = 1; i <= sig; ++i){
		MinAllCost += TMinCost[i];
		int len = Tcnt[i].size();
		//printf("sig = %d : len = %d\n", i, len);
		LL cnt = 0;
		for(int j = 0; j < len; ++j){
			if(cost[Tcnt[i][j]] == TMinCost[i]) cnt++;
		}
		Method = Method * cnt % mod;
	}
	printf("%lld %lld\n", MinAllCost, Method);
	return 0;
}

Tarjan算法模板:(POJ2553)

int sig;//连通分量标号
int Top;//栈顶坐标
int Stack[maxn];//栈
int cnt;//dfn
int dfn[maxn], low[maxn];
vector<int> G[maxn];//存图
int vis[maxn];//标记
int color[maxn];//染色缩点

int degree[maxn];//出度 for poj 2553

void Tarjan(int x){
	vis[x] = 1;
	Stack[++Top] = x;
	low[x] = dfn[x] = ++cnt;
	int len = G[x].size();
	for(int i = 0; i < len; ++i){
		int v = G[x][i];
		if(vis[v] == 0) Tarjan(v);
		if(vis[v] == 1) low[x] = min(low[x], low[v]);
	}
	if(dfn[x] == low[x]){
		sig++;
		while(1){
			low[Stack[Top]] = sig;
			vis[Stack[Top]] = -1;
			color[Stack[Top]] = sig;
			if(Stack[Top--] == x) break;
		}
	}
}

int main(){
	int n, m;//n点m边
	while(~ scanf("%d", &n) && n){
		scanf("%d", &m);
		Top = 0;
		sig = 0;
		cnt = 0;
		for(int i = 1; i <= n; ++i) {
			G[i].clear();
			vis[i] = 0;
			color[i] = 0;
			dfn[i] = 0;
			low[i] = 0;
			degree[i] = 0;
			Stack[i] = 0;
		}		
		for(int i = 1; i <= m; ++i){
			int u, v;
			scanf("%d %d", &u, &v);
			G[u].pb(v);
		}
		for(int i = 1; i <= n; ++i){
			if(vis[i] == 0) Tarjan(i);
		}
		//-------------------------------
		for(int i = 1; i <= n; ++i){
			int len = G[i].size();
			for(int j = 0; j < len; ++j){
				int v = G[i][j];
				if(color[i] != color[v]) degree[color[i]]++;
			}
		}
		vector<int> ans;
		for(int i = 1; i <= sig; ++i){
			if(degree[i]) continue;
			for(int j = 1; j <= n; ++j){
				if(color[j] == i) ans.pb(j);
			}
		}
		sort(ans.begin(), ans.end());
		for(int i = 0; i < ans.size() - 1; ++i) printf("%d ", ans[i]);
		printf("%d\n", ans[ans.size() - 1]);
		//--------------poj2553---------
	}
	return 0;
}

 

发布了21 篇原创文章 · 获赞 8 · 访问量 1799
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览