[BZOJ 1369][树形DP] Gem

树形DP题

这题乍一看会觉得:“这啥玩意,染遍色不就出来了吗,这么水?”

然后就会发现直接WA掉

其实这题是个树形DP题,我们可以发现当类似“偶数个节点链接了两颗大子树”之类强制让你的结构中一大部分点使用 2 作为点权的情况时,仅用两个颜色并不能得到最优解。

而且这题的恶心之处在于它还是个结论题:可以证明按题意染色一棵树需要的颜色种数不超过log(n)

显然我是不会证的,但有神仙证出来了(而且机房You神也证出来了貌似%%%)


于是可以写树形DP:f[i][j] 表示 以 i 节点为根节点的一棵子树,当 i 节点的颜色(点权)为 j 时可得的最小点权和

而转移就很显然了:

  f[i][j] = ∑(min(f[k][l])) 其中 k 是 i 的子节点,l,j∈[1, 15] 且 l ≠ j

代码如下:

 1 # include <iostream>
 2 # include <cstdio>
 3 # include <cstring>
 4 # define MAXN 10005
 5 # define MAXINT 2<<20
 6 
 7 using namespace std;
 8 
 9 struct edge{
10     int u, v;
11     int next;
12 }e[MAXN <<2];
13 
14 int head[MAXN], cntEdge, f[MAXN][21];
15 
16 void addEdge(int u, int v){
17     e[cntEdge++].u = u;
18     e[cntEdge].v = v;
19     e[cntEdge].next = head[u];
20     head[u] = cntEdge;
21     return;
22 }
23 
24 void dfs(int now, int fa){
25     for(int i = head[now]; i; i = e[i].next)
26         if(e[i].v != fa)
27             dfs(e[i].v, now);
28 
29     for(int i = 1; i <= 20; i++)
30         for(int j = head[now]; j; j = e[j].next)
31             if(e[j].v != fa){
32                 int ans = MAXINT;
33                 for(int k = 1; k <= 20; k++)
34                     if(i != k)
35                         ans = min(ans, f[e[j].v][k]);
36                 f[now][i] += ans;
37             }
38     return;
39 }
40 
41 int main(){
42     int n, x, y, ans = MAXINT; 
43     scanf("%d", &n);
44     
45     for(int i = 1; i <= n; i++)
46         for(int j = 1; j <= 20; j++)
47             f[i][j] = j; // 初始化
48     
49 
50     for(int i = 1; i < n; i++){
51         scanf("%d%d", &x, &y);
52         addEdge(x, y);
53         addEdge(y, x);
54     }
55 
56     dfs(1, 0);
57 
58     for(int i = 1; i <= 20; i++)
59         ans = min(ans, f[1][i]);
60 
61     printf("%d", ans);
62 
63     return 0;
64 }

复杂度大概是 O(log(n^2)) 的吧。。 这东西我根本不会算

以上

posted @ 2020-05-16 22:08  ChPu437  阅读(137)  评论(1编辑  收藏  举报