读QZC《分治算法在树的路径问题中的应用》

    一点一点看,即时更新。

    首先是点的分治,如何点的分治是重点,若随机找点作为根,最坏情况可能导致复杂度退化成O(N),故要找一个点,论文说此点叫重心,使得分出来的子树中,尽量保持平衡,这样需要O(N)的时间来进行预处理找出重心,然后分治的时候复杂度就变成了O( logn )。

    POJ1655 Balancing Act

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1655

    POJ3107 Godfather

    http://acm.pku.edu.cn/JudgeOnline/problem?id=3107

    POJ2378  Tree Cutting

    http://acm.pku.edu.cn/JudgeOnline/problem?id=2378

    就是解决这个找重心的问题,取子树中的最大值来作为balance量

    做法是可以任意选一点为根,然后DFS,以此根为一棵有根树,记录每个节点所在子树的节点数 sum[ i ] 。那么 balance[ i ]的值为子树中最大的 sum[ son(i) ]值,还要比较的还有n - sum[ i ],这个是由于断掉该点后,该点以上的树的节点数。

ContractedBlock.gifExpandedBlockStart.gifPOJ 1655 

 
     
1 #include < iostream >
2 #include < vector >
3   using namespace std;
4
5   const int maxn = 20000 + 1 ;
6
7 vector < int > tt[maxn];
8   int n, balance[maxn], sum[maxn];
9 bool visit[maxn];
10
11 void init()
12 {
13 for ( int i = 0 ; i < n; i ++ )
14 {
15 visit[i] = 0 ;
16 sum[i] = 0 ;
17 balance[i] = 0 ;
18 tt[i].clear();
19 }
20 }
21
22 void DFS( int u)
23 {
24 visit[u] = 1 ;
25 int sz = tt[u].size();
26 for ( int i = 0 ; i < sz; i ++ )
27 {
28 int v = tt[u][i];
29 if (visit[v])
30 continue ;
31 DFS(v);
32 sum[u] += sum[v];
33 balance[u] = max(sum[v] , balance[u]);
34 }
35 sum[u] ++ ;
36 }
37
38 void solve()
39 {
40 DFS( 1 );
41 for ( int i = 1 ; i <= n; i ++ )
42 {
43 balance[i] = max(n - sum[i] , balance[i]);
44 }
45 int mn = balance[ 1 ] , index = 1 ;
46 for ( int i = 2 ; i <= n; i ++ )
47 {
48 if (balance[i] < mn)
49 {
50 mn = balance[i];
51 index = i;
52 }
53 }
54 cout << index << ' ' << mn << endl;
55 }
56
57 int main()
58 {
59 freopen( " in " , " r " , stdin);
60 int cas;
61 cin >> cas;
62 while (cas -- )
63 {
64 cin >> n;
65 init();
66 int u , v;
67 for ( int i = 0 ; i < n - 1 ; i ++ )
68 {
69 cin >> u >> v;
70 tt[u].push_back(v);
71 tt[v].push_back(u);
72 }
73 solve();
74 }
75 return 0 ;
76 }
77

 

    接下来是分治,既每次找重心,此时递归深度变小,处理这棵树,然后就可以删除这个根,进行找子树的重心。虽然这样找重心后处理会可能出现找很多次重心,但由于递归深度变小了,所以每次找的时间是变小的。

    POJ 1741 Tree

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1741

    这个我TLE无数次,其实做法是对的,只是由于在找根的时候,用了一个visit数组来标记访问过的点,所以每次要memset,导致TLE,其实可以直接找标志不要回去父亲节点即可。

    1、先DFS找出这棵树的重心,然后再一次DFS得到以这个重心为根的时候,各个点到达根的距离。

    2、然后求出距离当中两者之和 <= k的对数。

    3、这些对数之中,可能存在经过某个子树到根后,又返回来,这个时候需要DFS一遍,找出以这个子树为根的时候,各个点到达之前的根的距离。

    4、减去这些距离当中两者之和 <= k的对数。

    5、以这个重心为根,删掉这个重心,然后对子树进行处理,返回1。

    POJ 1987 Distance Statistics

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1987

    同上题一模一样。

    POJ 2114 Boatherds

    http://acm.pku.edu.cn/JudgeOnline/problem?id=2114

    和上题基本一样,就是求权值刚好等于k的路径存不存在

ContractedBlock.gifExpandedBlockStart.gifPOJ 1741 

 
     
1 #include < cstdio >
2 #include < algorithm >
3
4 using namespace std;
5
6 const int maxn = 20000 + 1 ;
7
8 struct node
9 {
10 int v , w;
11 int balance , sum , deepth;
12 node * next;
13 } * adj[maxn] , edge[maxn] , tt[maxn];
14
15 int n , k , ans , len, D[maxn], size[maxn], sign[maxn];
16 bool used[maxn];
17
18 int e_num;
19 void init()
20 {
21 e_num = ans = 0 ;
22 for ( int i = 1 ; i <= n; i ++ )
23 {
24 adj[i] = NULL;
25 used[i] = false ;
26 }
27 }
28
29 void add_edge( int u , int v , int w)
30 {
31 node * ptr = & edge[ e_num ++ ] ;
32 ptr -> v = v;
33 ptr -> w = w;
34 ptr -> next = adj[u];
35 adj[u] = ptr;
36 }
37
38 int sz;
39 void DFS( int u, int f)
40 {
41 tt[u].sum = tt[u].balance = 0 ;
42 for (node * ptr = adj[u]; ptr; ptr = ptr -> next)
43 {
44 int v = ptr -> v;
45 if (v == f || used[v])
46 continue ;
47 DFS(v , u);
48 tt[u].sum += tt[v].sum;
49 tt[u].balance = max(tt[u].balance , tt[v].sum);
50 }
51 tt[u].sum ++ ;
52 size[ sz ] = tt[u].balance;
53 sign[ sz ++ ] = u;
54 }
55
56 int GET_ROOT( int root)
57 {
58 sz = 0 ;
59 DFS(root , 0 );
60 int mn = 0x7fffffff , total = tt[root].sum;
61 for ( int i = 0 ; i < sz; i ++ )
62 {
63 size[i] = max(size[i] , total - size[i]);
64 if (size[i] < mn)
65 {
66 mn = size[i];
67 root = sign[i];
68 }
69 }
70 return root;
71 }
72
73 void GET_DEEP( int u , int deep , int f)
74 {
75 D[len ++ ] = deep;
76 for (node * ptr = adj[u]; ptr; ptr = ptr -> next)
77 {
78 int v = ptr -> v;
79 int w = ptr -> w;
80 if (v == f || used[v])
81 continue ;
82 GET_DEEP(v , w + deep , u);
83 }
84 }
85
86 void doit1( int root)
87 {
88 sort(D , D + len);
89 int l = 0 , r = len - 1 ;
90 while (l < r)
91 {
92 if (D[l] + D[r] <= k)
93 {
94 ans += (r - l);
95 l ++ ;
96 } else
97 r -- ;
98 }
99 }
100
101 void doit2( int root)
102 {
103 used[root] = true ;
104 for (node * ptr = adj[root]; ptr; ptr = ptr -> next)
105 {
106 if (used[ptr -> v])
107 continue ;
108 len = 0 ;
109 GET_DEEP(ptr -> v , ptr -> w , root);
110 sort(D , D + len);
111 int l = 0 , r = len - 1 ;
112 while (l < r)
113 {
114 if (D[l] + D[r] <= k)
115 {
116 ans -= (r - l);
117 l ++ ;
118 } else
119 r -- ;
120 }
121 }
122 }
123
124 void solve( int root)
125 {
126 root = GET_ROOT(root);
127 len = 0 ;
128 GET_DEEP(root , 0 , 0 );
129 doit1(root);
130 doit2(root);
131 for (node * ptr = adj[root]; ptr; ptr = ptr -> next)
132 {
133 if (used[ptr -> v])
134 continue ;
135 solve(ptr -> v);
136 }
137 }
138
139 int main()
140 {
141 while (scanf( " %d %d " , & n, & k) != EOF)
142 {
143 if (n == 0 && k == 0 )
144 break ;
145 init();
146 for ( int i = 1 ; i <= n - 1 ; i ++ )
147 {
148 int u , v , w;
149 scanf( " %d %d %d " , & u, & v, & w);
150 add_edge(u , v , w);
151 add_edge(v , u , w);
152 }
153 solve( 1 );
154 printf( " %d\n " , ans);
155 }
156 return 0 ;
157 }
158

 

转载于:https://www.cnblogs.com/xiao_wu/archive/2010/06/01/1748728.html

你可能感兴趣的:(读QZC《分治算法在树的路径问题中的应用》)