Time Limit: 2000MS | Memory Limit: 65536K | |
Description
Input
Output
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
题目大意:给定一个有向图,判断是否对任意两点u,v,有u可达v 或 v可达u?
先找出所有的强连通分量,则各强连通分量内是相互可达的,只用判断强连通分量之间是否至少单向可达
将各强连通分量缩成一点,建立新图,则新图是DAG
刚开始也认为只要从一个入度为0的点到出度为0的点的长度为总点数,就满足题意,否则不行,但是看见有人说不是只有一条链的情况下会输出Yes,于是放弃了
最终用拓扑排序AC,若某次队列中点的数目大于1(则这些点之间不能单向可达),则输出No,否则输出Yes
没有初始化indeg数组,导致WA了很久都没看出来...
#include <cstdio> #include <cstring> #include <vector> using namespace std; const int MAXN=1005; int n,m,num,cnt,ans,top,head,tail; int stak[MAXN],que[MAXN]; int low[MAXN],dfn[MAXN],color[MAXN],indeg[MAXN];//biocks[i]表示删掉i点后,能形成的连通块数 vector<int> g[MAXN]; bool isIn[MAXN],mp[MAXN][MAXN]; void Tarjan(int u,int p) { stak[++top]=u; isIn[u]=true; dfn[u]=low[u]=++num; int v; for(int i=0;i<g[u].size();++i) { v=g[u][i]; if(dfn[v]==0) { Tarjan(v,u); low[u]=min(low[u],low[v]); } else if(isIn[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } if(dfn[u]==low[u]) { ++cnt; do { v=stak[top--]; isIn[v]=false; color[v]=cnt;//同一个强连通分量缩成一个点 } while(v!=u); } } bool TopoSort() { head=tail=0; for(int i=1;i<=cnt;++i) if(indeg[i]==0) que[tail++]=i; if(tail-head>1)//如果入度为0的点超过一个,则这些点不能相互单向可达 return false; int u; while(head!=tail) { u=que[head++]; for(int i=1;i<=cnt;++i) { if(mp[u][i]) { --indeg[i]; if(indeg[i]==0)//入度为0的点入队 que[tail++]=i; } } if(tail-head>1)//如果入度为0的点超过一个,则这些点不能相互单向可达 return false; } return true; } int main() { int T,s,e; scanf("%d",&T); while(T-->0) { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { g[i].clear(); dfn[i]=indeg[i]=0; isIn[i]=false; } while(m-->0) { scanf("%d%d",&s,&e); g[s].push_back(e); } cnt=num=top=0;//cnt表示强连通分量 for(int i=1;i<=n;++i) { if(dfn[i]==0) Tarjan(i,0); } memset(mp,false,sizeof(mp)); for(int i=1;i<=n;++i) { for(int j=0;j<g[i].size();++j) { s=color[i]; e=color[g[i][j]]; if(s!=e&&!mp[s][e]) {//建立新图,不考虑重边 mp[s][e]=true; ++indeg[e]; } } } printf("%s\n",TopoSort()?"Yes":"No"); } return 0; }
#include <cstdio> #include <cstring> #include <vector> using namespace std; const int MAXN=1005; int n,m,num,cnt,ans,top; int stak[MAXN]; int low[MAXN],dfn[MAXN],color[MAXN],dp[MAXN];//biocks[i]表示删掉i点后,能形成的连通块数 vector<int> g[MAXN],mp[MAXN]; bool isIn[MAXN],haveIndeg[MAXN]; void Tarjan(int u,int p) { stak[++top]=u; isIn[u]=true; dfn[u]=low[u]=++num; int v; for(int i=0;i<g[u].size();++i) { v=g[u][i]; if(dfn[v]==0) { Tarjan(v,u); low[u]=min(low[u],low[v]); } else if(isIn[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } if(dfn[u]==low[u]) { ++cnt; do { v=stak[top--]; isIn[v]=false; color[v]=cnt;//同一个强连通分量缩成一个点 } while(v!=u); } } void dfs(int u) { if(dp[u]!=-1) return ; if(mp[u].size()==0) {//如果点u是叶子节点,则其能经过的点数为1 dp[u]=1; return ; } for(int i=0;i<mp[u].size();++i) { dfs(mp[u][i]); dp[u]=max(dp[u],dp[mp[u][i]]+1);//点u的经过的点的个数为其各子结点经过的点的个数+1 } } bool Judge() { cnt=num=top=0;//cnt表示强连通分量 for(int i=1;i<=n;++i) { if(dfn[i]==0) Tarjan(i,0); } int s,e,sta; memset(mp,false,sizeof(mp)); for(int i=1;i<=n;++i) { for(int j=0;j<g[i].size();++j) { s=color[i]; e=color[g[i][j]]; if(s!=e) {//建立新图 mp[s].push_back(e); haveIndeg[e]=true;; } } } num=0; for(int i=1;i<=cnt;++i) { dp[i]=-1; if(!haveIndeg[i]) { sta=i; ++num; } } if(num>1) return false; dfs(sta); return dp[sta]==cnt;//判断入度为0的点能达到的最大深度是否等于点的个数 } int main() { int T,s,e; scanf("%d",&T); while(T-->0) { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { g[i].clear(); mp[i].clear(); dfn[i]=0; isIn[i]=haveIndeg[i]=false; } while(m-->0) { scanf("%d%d",&s,&e); g[s].push_back(e); } printf("%s\n",Judge()?"Yes":"No"); } return 0; }