zoukankan      html  css  js  c++  java
  • Tarjan算法专练

    1.迷宫城堡

    题意:给一个图判断是否是强连通图。

    题解:利用Tarjan计算图中强连通分量的个数,如果为1则是强连通图,否则不是。

    #include<bits/stdc++.h>
    
    using namespace std;
    const int N = 2e4+100;
    typedef long long ll;
    vector<int> G[N];
    bool is_instack[N];
    int dfn[N],low[N];
    stack<int> sta;
    int n,m,index,scc;
    void init(){
    	index=scc=0;
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(is_instack,0,sizeof(is_instack));
    	while(!sta.empty()) sta.pop();
    	for(int i=1;i<=n;i++) G[i].clear();
    }
    void Tarjan(int u){
    	dfn[u]=low[u]=++index;
    	sta.push(u);is_instack[u]=1;
    	for(auto v:G[u]){
    		if(!dfn[v]){
    			Tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(is_instack[v]){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(low[u]==dfn[u]){
    		++scc;
    		while(sta.top()!=u){
    			is_instack[sta.top()]=0;
    			sta.pop();
    		}
    	}
    }
    int main(){
    	while(scanf("%d %d",&n,&m)){
    		if(n+m==0) break;
    		init();
    		int u,v;
    		for(int i=0;i<m;i++){
    			scanf("%d %d",&u,&v);
    			G[u].push_back(v);
    		}
    		for(int i=1;i<=n;i++){
    			if(!dfn[i]) Tarjan(i);
    		}
    		if(scc==1) puts("Yes");
    		else puts("No");
    	}
    	return 0;
    }
    

    2.Proving Equivalences

    题意:给定一个有向图,求最少加几条有向边使得整个图成为强连通。

    题解:Tarjan缩点之后计算入读为(0)的个数(a)和出度为(0)的个数(b),取最大值(max(a,b)),注意如果已经是强连通图了,则答案为(0).

    #include<bits/stdc++.h>
    
    using namespace std;
    const int N = 1e5+100;
    vector<int> G[N],color[N];
    stack<int> sta;
    int dfn[N],low[N];
    int id[N],od[N];
    bool is_instack[N];
    int scc[N],nscc,index;
    int n,m;
    void init(){
    	nscc=index=0;
    	memset(scc,0,sizeof(scc));
    	memset(dfn,0,sizeof(dfn));
    	memset(id,0,sizeof(id));
    	memset(od,0,sizeof(od));
    	memset(is_instack,0,sizeof(is_instack));
    	memset(low,0,sizeof(low));
    	for(int i=1;i<=n;i++) G[i].clear(),color[i].clear();
    	while(!sta.empty()) sta.pop();
    }
    void Tarjan(int u){
    	low[u]=dfn[u]=++index;
    	sta.push(u);is_instack[u]=1;
    	for(auto v:G[u]){
    		if(!dfn[v]){
    			Tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(is_instack[v]){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(low[u]==dfn[u]){
    		++nscc;
    		while(1){
    			int temp=sta.top();
    			scc[temp]=nscc;
    			sta.pop();
    			is_instack[temp]=0;
    			if(temp==u) break;
    		}
    	}
    }
    int main(){
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d %d",&n,&m);
    		init();
    		int u,v;
    		for(int i=1;i<=m;i++){
    			scanf("%d %d",&u,&v);
    			G[u].push_back(v);
    		}
    		for(int i=1;i<=n;i++){
    			if(!dfn[i]) Tarjan(i);
    		}
    		//cerr<<nscc<<endl;
    		if(nscc==1){
    			puts("0");
    		} 
    		else{
    			for(int i=1;i<=nscc;i++) id[i]=od[i]=1;
    			for(int i=1;i<=n;i++){
    				for(auto j:G[i]){
    					if(scc[i]!=scc[j]){
    						id[scc[j]]=od[scc[i]]=0;
    					}
    				}
    			} 
    			int n1=0;int n2=0;
    			for(int i=1;i<=nscc;i++){
    				n1+=id[i];n2+=od[i];
    			}
    			printf("%d
    ",max(n1,n2));
    		}
    	}
    	return 0;
    }
    

    3.Summer Holiday

    题意:给你一个有向图,每个点都有一个权值,你需要在这个图中选择几个点使得利用这几个点可以遍历完整个图,同时还要满足选择的这几个点的权值和最小。

    题解:Tarjan缩点,然后在缩点的时候把点的权值缩成所在的强连通分量里面权值最小的,然后寻找入度为0的点的个数,即为答案。
    {% fold 点击显/隐内容 %}

    #include<bits/stdc++.h>
    
    using namespace std;
    const int N = 1e5+100;
    typedef long long ll;
    const int INF = 0x3f3f3f3f;
    int n,m,scc,index;
    vector<int> G[N];
    ll w[N],low[N],dfn[N],minn[N],color[N],id[N];
    bool is_instack[N];stack<int> sta;
    void init(){
    	scc=index=0;
    	memset(low,0,sizeof(low));
    	memset(dfn,0,sizeof(dfn));
    	memset(color,0,sizeof(color));
    	memset(minn,INF,sizeof(minn));
    	memset(is_instack,0,sizeof(is_instack));
    	for(int i=1;i<=n;i++) G[i].clear();
    	while(!sta.empty()) sta.pop();
    }
    void Tarjan(int u){
    	low[u]=dfn[u]=++index;
    	sta.push(u);is_instack[u]=1;
    	for(auto v:G[u]){
    		if(!dfn[v]){
    			Tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(is_instack[v]){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(low[u]==dfn[u]){
    		++scc;
    		while(1){
    			int temp=sta.top();
    			color[temp]=scc;
    			minn[scc]=min(minn[scc],w[temp]);
    			is_instack[temp]=0;
    			sta.pop();
    			if(temp==u) break;
    		}
    	}
    }
    int main(){
    	while(scanf("%d %d",&n,&m)!=EOF){
    		init();
    		for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    		int u,v;
    		for(int i=1;i<=m;i++){
    			scanf("%d %d",&u,&v);
    			G[u].push_back(v);			
    		}
    		for(int i=1;i<=n;i++){
    			if(!dfn[i]) Tarjan(i);
    		}
    		for(int i=1;i<=scc;i++) id[i]=1;
    		for(int i=1;i<=n;i++){
    			for(auto j:G[i]){
    				if(color[i]!=color[j]){
    					id[color[j]]=0;
    				}
    			}
    		}
    		ll ans1,ans2;ans1=ans2=0;
    		for(int i=1;i<=scc;i++){
    			if(id[i]){
    				ans1++;
    				ans2+=minn[i];
    			}
    		}
    		printf("%lld %lld
    ",ans1,ans2);
    	}
    	return 0;
    }
    

    {% endfold %}

    4.Intelligence System

    题意:一个人际关系网,0号可以联系上任意一个人,如果两个人可以直接或间接的互相联系,那么这两个人的消费为0,求0号要联系上每个人最小的消费。

    题解: Tarjan缩点,求最小树形图,由于题目保证有解,因此只需要统计每个点入边权值最小的即可。0所在的强连通分量不用考虑。

    #include<bits/stdc++.h>
    
    using namespace std;
    const int N = 50005	;
    typedef long long ll;
    const ll INF = 0x3f3f3f3f;
    ll low[N],dfn[N],minn[N],color[N];
    bool  is_instack[N];
    ll n,m,scc,index;
    stack<ll> sta;
    vector<pair<ll,ll> > G[N];
    void init(){
    	scc=index=0;
    	for(int i=0;i<=n;i++){
    		is_instack[i]=0;
    		low[i]=0;
    		dfn[i]=0;
    		minn[i]=INF;
    		G[i].clear();
    	}
    	while(!sta.empty()) sta.pop();
    }
    void Tarjan(ll u){
    	low[u]=dfn[u]=++index;
    	is_instack[u]=1;sta.push(u);
    	for(auto V:G[u]){
    		ll v=V.first;
    		if(!dfn[v]){
    			Tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(is_instack[v]){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(low[u]==dfn[u]){
    		scc++;
    		while(1){
    			ll temp=sta.top();
    			color[temp]=scc;
    			is_instack[temp]=0;
    			sta.pop();
    			if(temp==u) break;
    		}
    	}
    }
    int main(){
    	while(scanf("%lld %lld",&n,&m)!=EOF){
    		map<pair<ll,ll>,ll> WW;
    		//pair<ll,ll> pnow;
    		init();
    		ll u,v,w;
    		for(ll i=1;i<=m;i++){
    			scanf("%lld %lld %lld",&u,&v,&w);
    			//pnow.first=u;pnow.second=v;
    			//if(!WW[pnow]) WW[pnow]=w;
    			//else WW[pnow]=min(WW[pnow],w);
    			G[u].push_back(make_pair(v,w));
    		}
    		long long ans=0;
    		for(ll i=0;i<n;i++){
    			if(!dfn[i]) Tarjan(i);
    		}
    		for(ll i=0;i<n;i++){
    			for(auto j:G[i]){
    				ll x=color[i];
    				ll y=color[j.first];
    				if(x!=y) minn[y]=min(minn[y],(ll)j.second);
    			}
    		}
    		for(ll i=1;i<=scc;i++){
    			if(i!=color[0]) ans+=minn[i];
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    模拟器 | 如何安装ENSP,附上最详细的步骤,含安装软件!
    《平凡的世界》孙少平给妹妹孙兰香的信
    Date类添加一个新的方法,用prototype
    jquery动画相关函数
    斐波那契数列 递归调用
    怎样才能升天?
    jquery tabs切换插件
    vmware桥接共享的问题
    C#,mysql 添加数据的问题
    一根神奇的网线
  • 原文地址:https://www.cnblogs.com/codancer/p/12232291.html
Copyright © 2011-2022 走看看