• 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;
    }
    
  • 相关阅读:
    [vp]ARC068
    [vp]ARC067
    Vision transformer
    rosetta使用silent格式储存PDB结构,节省本地存储
    CentOS7下安装JDK详细过程
    jdk下载Oracle共享账号
    虚拟机地址发生变化
    字节跳动面试题,给你一个每一项都是数值混乱顺序的数组,只要里面正确顺序的值输出。如[5,1,3,6,2,7],只要[1,2,7]
    spring boot web 第一个项目新建
    xmind-excel
  • 原文地址:https://www.cnblogs.com/codancer/p/12232291.html
Copyright © 2020-2023  润新知