题意:判断一个图是否为仙人掌图
分析:直观的说,仙人掌图就是一个一个的圈直接“粘”在一起的图,圈 之间没有公共边。
仙人掌图有这样三个性质:
性质1 仙人掌图的DFS树没有横向边。----------------------------------
性质2 Low(v)<=DFN(u) (v是u的儿子) (即没有桥)
性质3 设某个点v有a(u)个儿子的Low值小于DFS(u),同时u自己有b(u)条逆向边。那么a(u)+b(u)<2。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
const int MAXN = 20010;
vector<int> g[MAXN];
stack<int> st;
int n,index,dfn[MAXN],low[MAXN],num,cc;
bool vis[MAXN],instack[MAXN];
void init()
{
for(int i=0;i<n;i++)
{
g[i].clear();
instack[i]=vis[i]=false;
dfn[i]=low[i]=-1;
}
while(!st.empty())
st.pop();
}
bool tarjan(int u)
{
int v,cnt=0;
cc++;
dfn[u]=low[u]=index++;
instack[u]=true;
st.push(u);
for(int i=0;i<g[u].size();i++)
{
v=g[u][i];
if(vis[v]) return false;//判断是否存在横边,vis{]是标记一点的子树是否遍历完,所以vis{u]的值要在遍历完子树才可以标记为true
if(dfn[v]==-1)
{
if(!tarjan(v))
return false;
if(low[v]>dfn[u])//存在桥
return false;
if(low[v]<dfn[u]) //a(u) 的值
cnt++;
if(cnt>=2) return false;
low[u]=min(low[u],low[v]);
}
else if(instack[v])
{
low[u]=min(low[u],dfn[v]);
cnt++;//b(u) 的值
if(cnt>=2) return false;
}
}
if(dfn[u]==low[u])
{
do
{
v=st.top();
instack[v]=false;
st.pop();
}while(v!=u);
num++;
if(num>=2) //其实这样有一点多此一举,num>=2也就是存在桥了……
return false;
}
vis[u]=true;
return true;
}
int main()
{
int T,a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
while(true)
{
scanf("%d %d",&a,&b);
if(a==0 && b==0)
break;
g[a].push_back(b);
}
index=num=cc=0;
if(!tarjan(0))
{
puts("NO");
continue;
}
if(cc!=n)
puts("NO");
else puts("YES");
}
return 0;
}