tarjan算法

tarjan算法

记1月9日至1月17日,tarjan算法暂时告一段落。

个人体会:

主要功能其一:求解连通分量,将图转化成树。树的特性很多,利用树的特性解题。

主要功能其二:求出并理解桥和割点,掌握生成和消除桥或割点的方法,算答案或算贡献。

算法能用的东西不多,但写题的过程还是很欢乐的,代码不长不短,解题要些思维。

放上几个板子:

tarjan求强连通分量(缩点)

#include<iostream>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
#define ll long long
ll n,m,a[10007],u,v;
ll dfn[10007],low[10007],vis[10007],sum[10007],pos[10007],cnt;
ll dp[10007];
stack<ll>sa;
vector<ll>ho[10007];
vector<ll>aq[10007];
void dfs(ll p){
	dfn[p]=low[p]=++cnt;
	sa.push(p);
	vis[p]=1;
	for(int i=0;i<ho[p].size();i++){
		ll to=ho[p][i];
		if(dfn[to]==0){
			dfs(to);
			low[p]=min(low[p],low[to]);
		}
		else if(vis[to]==1){
			low[p]=min(low[p],dfn[to]);
		}
	}
	if(low[p]==dfn[p]){
		while(!sa.empty()){
			ll now=sa.top();
			sa.pop();
			vis[now]=0;
			aq[p].push_back(now);
			sum[p]+=a[now];
			pos[now]=p;
			if(now==p){
				break;
			}
		}
	}
}
ll fin(ll p){
	ll res=0;
	if(dp[p]!=-1){
		return dp[p];
	}
	for(int i=0;i<aq[p].size();i++){
		ll now=aq[p][i];
		for(int j=0;j<ho[now].size();j++){
			ll to=pos[ho[now][j]];
			if(pos[to]!=pos[p]){
				res=max(res,fin(to));
			}
		}
	}
	return dp[p]=res+sum[p];
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&u,&v);
		ho[u].push_back(v);
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			dfs(i);
		}
	}
	ll ans=0;
	memset(dp,-1,sizeof(dp));
	for(int i=1;i<=n;i++){
		ans=max(ans,fin(pos[i]));
	}
	printf("%lld\n",ans);
}

tarjan求桥

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
ll n,m,u,v;
ll dfn[100007],low[100007],cnt;
vector<ll>ho[100007];
vector<pair<ll,ll> >ans;
void tarjan(ll p,ll f){
	dfn[p]=low[p]=++cnt;
	for(int i=0;i<ho[p].size();i++){
		ll to=ho[p][i];
		if(to==f)continue;
		if(dfn[to]==0){
			tarjan(to,p);
			low[p]=min(low[p],low[to]);
			if(low[to]>dfn[p]){
				ans.push_back({min(to,p),max(p,to)});
			}
		}
		else{
			low[p]=min(low[p],dfn[to]);
		}
	}

}
void init(){
	ans.clear();
	cnt=0;
	for(int i=0;i<n;i++){
		ho[i].clear();
		dfn[i]=low[i]=0;
	}
}
int main(){
	while(~scanf("%lld",&n)){
		init();
		for(int i=1;i<=n;i++){
			char ch;
			scanf("%lld (%lld)",&u,&m);
			while(m--){
				scanf(" %lld",&v);
				if(u>=v)continue;
				ho[u].push_back(v);
				ho[v].push_back(u);
			}
		}
		for(int i=0;i<n;i++){
			if(dfn[i]==0){
				tarjan(i,-1);
			}
		}
		sort(ans.begin(),ans.end());
		printf("%d critical links\n",ans.size());
		for(int i=0;i<ans.size();i++){
			printf("%lld - %lld\n",ans[i].first,ans[i].second);
		}puts("");
	}
	return 0;
}

tarjan求割点

#include<iostream>
#include<vector>
#include<set>
using namespace std;
#define ll long long
ll n,u,v;
ll dfn[107],low[107],cnt;
vector<ll>ho[107];
set<ll>sa;
void tarjan(ll p,ll k){
	ll tot=0;
	dfn[p]=low[p]=++cnt;
	for(int i=0;i<ho[p].size();i++){
		ll to=ho[p][i];
		if(dfn[to]==0){
			tot++;
			tarjan(to,0);
			low[p]=min(low[p],low[to]);
			if(low[to]>=dfn[p]&&k==0){
				sa.insert(p);
			}
		}
		else{
			low[p]=min(low[p],dfn[to]);
		}
	}
	if(k==1&&tot>=2){
		sa.insert(p);
	}
}
int main(){
	while(~scanf("%lld",&n)){
		if(n==0)break;
		sa.clear();
		for(int i=1;i<=n;i++){
			ho[i].clear();
			dfn[i]=0;
			low[i]=0;
			cnt=0;
		}
		while(1){
			scanf("%lld",&u);
			if(u==0)break;
			while(1){
				scanf("%lld",&v);
				ho[u].push_back(v);
				ho[v].push_back(u);
				if(getchar()=='\n'){
					break;
				}
			}
		}
		for(int i=1;i<=n;i++){
			if(dfn[i]==0){
				tarjan(i,1);
			}
		}
		printf("%lld\n",sa.size());
	}
}

tarjan求双连通分量

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
#define ll long long
ll n,m,u,v,a[500007];
ll dfn[500007],low[500007],pos[500007],ans[500007],cnt;
vector<ll>ho[500007];
vector<ll>aq[500007];
vector<ll>ans2;
stack<ll>sa;
void tarjan(ll p,ll f){
	dfn[p]=low[p]=++cnt;
	sa.push(p);
	for(int i=0;i<ho[p].size();i++){
		ll to=ho[p][i];
		if(to==f)continue;
		if(dfn[to]==0){
			tarjan(to,p);
			low[p]=min(low[p],low[to]);
		}
		else{
			low[p]=min(low[p],dfn[to]);
		}
	}
	if(low[p]==dfn[p]){
		while(!sa.empty()){
			ll lin=sa.top();
			sa.pop();
			pos[lin]=p;
			ans[p]^=a[lin];//一个双连通分量的权值异或和
			aq[p].push_back(lin);
			if(lin==p){
				break;
			}
		}
	}
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&u,&v);
		ho[u].push_back(v);
		ho[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			tarjan(i,0);
		}
	}
	for(int i=1;i<=n;i++){
		if(pos[i]!=i)continue;
		else{
			ans2.push_back(ans[i]);
		}
	}
	sort(ans2.begin(), ans2.end());
	for(int i=0;i<ans2.size();i++){
		printf("%lld\n",ans2[i]);
	}
}
posted @ 2021-01-17 20:00  ccsu_madoka  阅读(75)  评论(0编辑  收藏  举报