hdu--1269--tarjan<有疑惑>
这2天 每天学个新的算法吧...可以的话....
tarjan的代码 还是很容易写的 也很容易构图 但总觉得有些细节不那么好理解
怎么说呢 总觉得他定义的 low数组含义是有问题的
这是 原作者下的定义
定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号
DFN数组的定义 我觉得没问题...但是 Low[u]数组的定义与我代码检验出来的结果并不相同... 当low[x] low[y] low[z]...low[n]是相等的时候 我们说x y z …………n是在一个连通分量之中的 但是显然我们经过模拟 可以得到如下答案 -- 就是根据那张经典图
6 8
1 2
2 4
4 6
5 6
3 4
3 5
4 1
1 3
标号 i No[i] father[i]
1 1 1
2 6 5
3 2 1
4 5 1
5 3 3
6 4 4
所以 我想low数组的定义不太恰当..
然后 另外一篇博客上是这样介绍的
这样,我们用low值记录该点所在强连通子图对应的搜索子树的根节点的Dfn值。注意,该子树中的元素在栈中一定是相邻的,且根节点在栈中一定位于所有子树元素的最下方
注意下 这边指的最下方 是指在栈这个数据结构之中..因为根结点首先被push进去 然后stack元素入栈是压上面的 所以根结点就在最下面了
如果遍历完整个搜索树后某个点的dfn值等于low值,则它是该搜索子树的根。这时,它以上(包括它自己)一直到栈顶的所有元素组成一个强连通分量'
这句话 是毫无疑问正确 这也是很重要的一点
强连通分量是由若干个环组成的。所以,当有环形成时(也就是搜索的下一个点已在栈中),我们将这一条路径的low值统一,即这条路径上的点属于同一个强连通分量。
这边 他的说法 又和我的结果有点矛盾...虽然在同一个连通块之中 但是low[]值并没有统一
***********************
还有 这题我做的时候 当我修改了下 father[u] = min( father[u] , No[v] ); ----------->father[u] = min( No[u] , No[v] );
任然是AC的 而且我测了几组的话 输出的dfn low数组也是相同的.....
我就更奇怪了 -.-
要是 我有些想法了 再随时来更新 写下来... 顺便 明天去和porker大神请教下..
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //横插边:连接到已经出栈的节点的边; 2 //后向边:连接到已在栈中节点的边; 3 //树枝边: 在搜索树中的边。 4 #include <iostream> 5 #include <cstring> 6 #include <stack> 7 #include <algorithm> 8 using namespace std; 9 10 int cnt , n , sum , index; 11 const int size = 10010; 12 int No[size]; 13 int father[size]; 14 bool vis[size]; 15 struct graph 16 { 17 int to; 18 int next; 19 }edge[size*10]; 20 int head[size]; 21 stack<int>s; 22 23 void init( ) 24 { 25 memset( head , -1 , sizeof(head) ); 26 memset( vis , false , sizeof(vis) ); 27 memset( No , 0 , sizeof(No) ); 28 } 29 30 void add( int st , int end ) 31 { 32 while( !s.empty() ) 33 s.pop(); 34 edge[cnt].to = end; 35 edge[cnt].next = head[st]; 36 head[st] = cnt++; 37 } 38 39 void tarjan( int u ) 40 { 41 int temp , v; 42 No[u] = father[u] = index++; 43 vis[u] = true; 44 s.push(u); 45 for( int i = head[u] ; ~i ; i = edge[i].next ) 46 { 47 v = edge[i].to; 48 if( !No[v] ) 49 { 50 tarjan(v); 51 father[u] = min( father[u] , father[v] ); 52 } 53 else if( vis[v] ) 54 { 55 father[u] = min( father[u] , No[v] ); 56 } 57 } 58 if( No[u] == father[u] ) 59 { 60 sum ++; 61 while(1) 62 { 63 temp = s.top(); 64 s.pop(); 65 vis[temp] = false; 66 if( No[temp]==father[temp] ) 67 break; 68 } 69 } 70 } 71 72 int main() 73 { 74 cin.sync_with_stdio(false); 75 int m , x , y; 76 while( cin >> n >> m &&(n||m) ) 77 { 78 init(); 79 sum = cnt = 0; 80 index = 1; 81 while( m-- ) 82 { 83 cin >> x >> y; 84 add(x,y); 85 } 86 for( int i = 1 ; i<=n ; i++ ) 87 { 88 if( !No[i] ) 89 { 90 tarjan(i); 91 } 92 } 93 //for( int i = 1 ; i<= n ;i++ ) 94 //{ 95 // cout << i << " " << No[i] << " " << father[i] << endl; 96 //} 97 if( sum==1 ) 98 cout << "Yes" << endl; 99 else 100 cout << "No" << endl; 101 } 102 return 0; 103 } 104 /* 105 6 8 106 1 2 107 2 4 108 4 6 109 5 6 110 3 4 111 3 5 112 4 1 113 1 3 114 标号 i No[i] father[i] 115 1 1 1 116 2 6 5 117 3 2 1 118 4 5 1 119 5 3 3 120 6 4 4 121 */
刚刚翻了下白书上是这样定义的:
low[u]为u及其后代能追溯到的最早<最先被发现>祖先点v的dnf[v]值
补上评论中的三个概念 有助理解:
横插边:连接到已经出栈的节点的边;
后向边:连接到已在栈中节点的边;
树枝边: 在搜索树中的边。
我大概明白了..----------->father[u] = min( No[u] , No[v] ); 这是肯定错误的 应该是数据问题吧
today:
回来 本身就是充满悲情色彩的一个词
因为 无论最终他有没有回来
但是 他曾经离开过你