这道题目挺有意思,amb大牛还发了那个强大的仙人掌图剖析的pdf,自己yy了一种解法,由于HDU上的数据够弱,
暂时没有发现自己的代码有啥错误的。网上各种代码错误,还各种ac了~~这道题的数据,汗~~
可以测测这一组数据:
1
4
0 1
1 2
2 0
2 3
3 2
3 0
0 0
正确输出:no
/* *State: HDU3594 78MS 2644K 3134 B C++ *题目大意: * 给一个有向图,判断这个有向图是否为仙人掌图。 * 仙人掌图满足:1、是强连通图 2、每条边都只属于一个环 *解题思路: * 判断强连通比较好做,主要是要判断每条边都属于一个环。 * 我想到了用块来判断,但是只用块判断过无向图,但是用笔模拟 * 了下,感觉有向图跟无向图都是一样的判断。 * 就保存了有向图跟无向图,想想一个强连通图是仙人掌图,那么 * 它从无向图上看一定是多个块之间靠一个点连接起来。那么好做 * 把它保存为无向图,然后求块,判断每个块中边的个数是否等于 * 块中点的个数,即可判断是否每条边只属于一个环。 *解题感想; * 1a了,没想到这么神奇,看来专注是必须的。 * 这道题目的数据比较水,该方法还有待验证。 * 该题值得慢慢品味 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 4 const int MAXN = 20005; 5 const int MAXE = 50005; 6 7 typedef struct _node 8 { 9 int v, next, isdir; 10 _node() :isdir(0) {}; 11 }N; 12 13 N edge[MAXE * 2]; 14 int head[MAXN], cntEdge, myS[MAXN], top; 15 int inS[MAXN], isScc; 16 17 int bMyS[MAXN], bTop; 18 int low[MAXN], dfn[MAXN], step; 19 20 int block[MAXN], isOneCir; 21 22 void init() 23 { 24 top = bTop = 0; 25 cntEdge = 0; 26 isScc = false; 27 isOneCir = true; 28 for(int i = 0; i < MAXN; i++) 29 { 30 inS[i] = 0; 31 head[i] = -1; 32 } 33 } 34 35 void addEdge(int u, int v) 36 { 37 edge[cntEdge].v = v; 38 edge[cntEdge].isdir = 1; 39 edge[cntEdge].next = head[u]; 40 head[u] = cntEdge++; 41 42 edge[cntEdge].v = u; 43 edge[cntEdge].next = head[v]; 44 edge[cntEdge].isdir = 0; 45 head[v] = cntEdge++; 46 } 47 48 void tarjan_scc(int n, int toln) 49 { 50 dfn[n] = low[n] = ++step; 51 myS[top++] = n; 52 inS[n] = 1; 53 for(int f = head[n]; f != -1; f = edge[f].next) 54 { 55 if(!edge[f].isdir) 56 continue; 57 int son = edge[f].v; 58 if(dfn[son] == -1) 59 { 60 tarjan_scc(son, toln); 61 low[n] = min(low[n], low[son]); 62 } 63 else if(inS[son] == 1) 64 low[n] = min(low[n], dfn[son]); 65 } 66 if(low[n] == dfn[n] && top != 0) 67 { 68 int _cnt = 0; 69 int tmp; 70 do 71 { 72 tmp = myS[--top]; 73 _cnt++; 74 }while(top != 0 && tmp != n); 75 if(_cnt == toln) 76 isScc = true; 77 } 78 } 79 80 void judge() 81 { 82 int vst[MAXN] = {0}; 83 for(int i = 1; i <= block[0]; i++) 84 { 85 vst[block[i]] = 1; 86 } 87 int u, v, sum = 0; 88 for(int i = 1; i <= block[0]; i++) 89 { 90 u = block[i]; 91 for(int f = head[u]; f != -1; f = edge[f].next) 92 { 93 v = edge[f].v; 94 if(vst[v]) 95 sum++; 96 } 97 } 98 sum /= 2; 99 if(isOneCir == true && sum != block[0]) 100 isOneCir = false; 101 } 102 103 //求块 104 void tarjan_block(int n) 105 { 106 dfn[n] = low[n] = ++step; 107 bMyS[bTop++] = n; 108 109 for(int f = head[n]; f != -1; f = edge[f].next) 110 { 111 int son = edge[f].v; 112 if(dfn[son] == -1) 113 { 114 tarjan_block(son); 115 low[n] = min(low[n], low[son]); 116 117 if(low[son] >= dfn[n]) 118 { 119 block[0] = 0; 120 int tmp; 121 while(true) 122 { 123 tmp = bMyS[bTop - 1]; 124 block[++block[0]] = tmp; 125 if(bMyS[--bTop] == son) 126 break; 127 } 128 block[++block[0]] = n; 129 judge(); 130 } 131 } 132 else 133 low[n] = min(low[n], dfn[son]); 134 } 135 } 136 137 int main(void) 138 { 139 #ifndef ONLINE_JUDGE 140 freopen("in.txt", "r", stdin); 141 #endif 142 143 int cas; 144 scanf("%d", &cas); 145 while(cas--) 146 { 147 init(); 148 int n, u, v; 149 scanf("%d", &n); 150 while(scanf("%d %d", &u, &v), u || v) 151 { 152 addEdge(u, v); 153 } 154 for(int i = 0; i < MAXN; i++) 155 dfn[i] = low[i] = -1; 156 step = 0; 157 tarjan_scc(0, n); 158 159 //强连通才继续判断 160 for(int i = 0; i < MAXN; i++) 161 dfn[i] = low[i] = -1; 162 step = 0; 163 164 if(isScc) 165 tarjan_block(0); 166 167 if(isScc && isOneCir) 168 printf("YES\n"); 169 else 170 printf("NO\n"); 171 } 172 return 0; 173 }