注意思路: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;
}

被折叠的 条评论
为什么被折叠?



