洛谷 P10944&算法竞赛 Going from u to v or from v to u? 题解

注意思路:tarjan 和 topo。没学过的右转 优快云 优先搜索。

题目简述

给定一个有向图,求任意两点 u,vu,vu,v 是否可以互相到达。

思路

因为是有向图,考虑算法——DFS,topo,tarjan

DFS 肯定是不可行了,时间复杂度为 O(Tnm)O(Tnm)O(Tnm)但是它过了……

topo 不能直接使用,考虑 tarjan 求出强连通分量。

将每个强连通分量构成一个顶点,发现询问有什么特征?

topo 一下可知,如果满足条件,那么入度为 000 和出度为 000 数取更大值为 000 且无环即可。

众所周知:本代码时间 O(n2)O(n^2)O(n2),请将缩点过程优化。

Code

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000;
int G[MAXN+1][MAXN+1],n,nG[MAXN+1][MAXN+1];
int in[MAXN+1];
int out[MAXN+1];
int dfn[MAXN+1],low[MAXN+1],cnt;
int cur=0,belong[MAXN+1],instack[MAXN+1];
stack<int>tk;
void tarjan(int u){
	dfn[u]=low[u]=++cnt;
	tk.push(u);
	instack[u]=true;
	for(int i=1;i<=n;i++){
		if(G[u][i]){
			if(dfn[i]==0){
				tarjan(i);
				low[u]=min(low[u],low[i]);
			}
			else if(instack[i])low[u]=min(low[u],dfn[i]);
		}
	}
	if(dfn[u]==low[u]){
		int tp;
		++cur;
		do{
			tp=tk.top();tk.pop();
//			cout<<tp<<'\n';
			belong[tp]=cur;
			instack[tp]=false;
		}while(tp!=u);
	}
	return;
}
bool topo(void){
    queue<int>Q;
    for(int i=1;i<=cur;i++){
        if(in[i]==0){
            Q.push(i);
        }
    }
    int ccc=0;
    while(!Q.empty()){
        if((int)Q.size()>1)return false;
        int F=Q.front();Q.pop();ccc++;
        for(int i=1;i<=n;i++){
            if(nG[F][i]){
                in[i]--;
                if(in[i]==0){
                    Q.push(i);
                }
            }
        }
    }
    return ccc==cur;
}
int solve(void){
    cur=0;cnt=0;
    while(!tk.empty())tk.pop();
    memset(G,0,sizeof G);
    memset(nG,0,sizeof nG);
    memset(in,0,sizeof in);
    memset(out,0,sizeof out);
    memset(dfn,0,sizeof dfn);
    memset(low,0,sizeof low);
    memset(belong,0,sizeof belong);
    memset(instack,0,sizeof instack);
    cin>>n;
    int m;cin>>m;
    for(int i=1;i<=m;i++){
    	int u,v;cin>>u>>v;
    	G[u][v]=1;
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
            cnt=0;
            tarjan(i);
		}
	}
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(belong[i]!=belong[j]&&G[i][j]&&!nG[belong[i]][belong[j]]){
                in[belong[j]]++,out[belong[i]]++;
                nG[belong[i]][belong[j]]=1;
            }
        }
    }
    int ccf=0,fcc=0;
    for(int i=1;i<=cur;i++){
        if(in[i]==0)ccf++;
        if(out[i]==0)fcc++;
    }
    if(max(ccf,fcc)<=1&&topo())puts("Yes");
    else puts("No");
    return 0;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)solve();
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值