Tarjan

学习:Tarjan

模板:(Gym102163B

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-05-02 16:05
 *-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue 
#define loop(c, a, b) for(int c = a; c <= b; ++c)
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

int dfn[maxn], low[maxn], color[maxn], vis[maxn];
int top, cnt, sig, n, m;
struct Edge{
	int next;
	int v;
	bool flag;
}e[maxn << 1];
int head[maxn], num;
vector<PII> G;
stack<int> S;

inline void Add_Edge(int u, int v){
	e[++num].v = v;
	e[num].next = head[u];
	e[num].flag = 0;
	head[u] = num;
}

inline void init(){
	num = -1;
	top = cnt = sig = ans = 0;
	G.clear();
	while(!S.empty()) S.pop();
	memset(d, -1, sizeof(d));
	memset(head, -1, sizeof head);
	memset(vis, 0, sizeof(vis));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	memset(color,0,sizeof color);
}

void Tarjan(int u){
	vis[u] = 1;
	S.push(u);
	dfn[u] = low[u] = ++cnt;
	for(int i = head[u]; i != -1; i = e[i].next){
		if(e[i].flag) continue;
		e[i].flag = e[i ^ 1].flag = 1;
		int v = e[i].v;
		if(!dfn[v]) {
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v])	low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u]){
		sig++;
		while(!S.empty()){
			int pos = S.top(); S.pop();
			color[pos] = sig;
			vis[pos] = 0;
			if(pos == u) break;
		}
	}
}

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		init();
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= m; ++i){
			int u, v;
			scanf("%d %d", &u, &v);
			G.pb(PII(u, v));
			Add_Edge(u, v);
			Add_Edge(v, u);
		}
		for(int i = 1; i <= n; ++i) if(!dfn[i]) Tarjan(i);
	}
	return 0;
}

例题: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值