題目鏈接:
http://acm.hdu.edu.cn/showproblem.php?pid=3594
題目大意:
仙人掌圖的判定。
本題中的仙人掌圖是一種強連通圖,使得每條邊至多在一個環上。
算法:
仙人掌算是一種蠻流行的圖論模型吧。
wiki 上 Cactus Graph 的定義是:
一個無向連通圖,任兩個簡單環至多含有一個公共點,等價於任一條邊至多屬於一個簡單環,等價於任何一個塊都是一個點或一個環。
但是在實際的題目和文檔中,好像說是無向圖的也有,說是有向圖的也有。說是邊至多在一個環中的也有,說是點至多在一個環中的也有。╮(╯▽╰)╭
另外我還找到了一篇介紹仙人掌圖的中文文檔。
文檔中介紹了仙人掌圖的三個性質:
性質 1: 仙人掌圖的 DFS樹沒有橫向邊。
性質 2: Low[v] <= DFS[u] (v 是 u 的兒子)
性質 3: 設某個點 u 有 a(u) 個兒子的 low 值小於 DFS[u] ,同時 u 自己有 b(u) 條反向邊。那麼 a(u) + b(u) < 2。
當然這道題只是要我們判定,做法是非常簡單的。
直接很據仙人掌圖的三個性質判定。
性質一和二都很好判定。
性質三的話,在沒有橫叉邊的情況下,
設某個點 u 有 a(u) 個兒子的 low 值小於 DFS[u] ,同時 u 自己有 b(u) 條反向邊,
那麼a(u) + b(u)就等於使得low[v] < dep[u]的邊(u,v)的數量(因爲沒橫叉邊的話dep[v ] < dep[u]就是反向邊嘛,然後 low[v] <= dep[v])
這代碼。。全部都是 if 啊啊啊。。。
代碼:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <climits>
#include <cmath>
#include <queue>
#include <vector>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN = 21000;
vector<int> mm[MAXN];
int low[MAXN], dep[MAXN];
bool ins[MAXN];
int cot[MAXN];
bool flg;
void dfs(int u, int p)
{
int tmp = dep[u] = low[u] = (p == -1) ? 0 : dep[p] + 1;
ins[u] = true;
for(int i = 0; i < mm[u].size(); i ++)
{
int v = mm[u][i];
if(dep[v] != -1 && !ins[v])
{
flg = false; //cactus圖無橫叉邊
}
if(dep[v] == -1)
{
dfs(v, u);
}
if(low[v] > dep[u])
{
flg = false; //cactus圖是強聯通的
}
if(low[v] < dep[u])
{
cot[u] ++;
if(cot[u] > 1)
{
flg = false; //設某個點u有a(u)個兒子的Low值小於DFS[u],同時 u 自己有 b(u)條逆向邊。那麼 a(u)+b(u)<2。
}
}
tmp = min(low[v], tmp);
}
low[u] = tmp;
ins[u] = false;
}
int main()
{
int cas;
scanf("%d", &cas);
for(int T = 1; T <= cas; T ++)
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i ++)
{
mm[i].clear();
}
memset(dep, -1, sizeof(dep));
memset(ins, 0, sizeof(ins));
memset(cot, 0, sizeof(cot));
flg = true;
int u, v;
while(scanf("%d %d", &u, &v), u || v)
{
mm[u].push_back(v);
}
dfs(0, -1);
for(int i = 0; i < n; i ++)
{
if(dep[i] == -1)
{
flg = false;
}
}
if(flg)
{
puts("YES");
}
else
{
puts("NO");
}
}
return 0;
}