[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)) 的吧。。 这东西我根本不会算
以上