很喜欢ppz说过的一句话,“下把一定过!”——Dinomax
悲惨经历:
南京站的时候,两个小时过了四个题,当时排在30多名。结果剩下的三个小时颗粒无收,怒打一铜。卡题就卡在这题上了,其实当时队友有H的思路,想等M过了之后试一会儿,结果一等就是三个小时(跪)。
我们两个人车轮战搞这个题,队友写了个splay,我用的优先队列搞贪心,感觉像是对的,但是样例第三个过不了,发现贪心想假了。隔壁也是两个人想的贪心都没过,一个用优先队列一个写线段树,不过原理都错了。
其实当时发现第三个样例是贪不了的时候想过dp,但是知识水平有限,觉得应该是O(n³)的复杂度,就没有头绪。后来发现过了的dp是O(n²),只能说自己太菜了。
题意:
给你一个n(2e3)个节点的树,将树上的节点进行删除,每删除一个节点的代价是当前节点的权值和它的直接儿子的权值之和。在删除开始前,可以无代价消去k个节点,消去节点的时候对应的父子关系都会断开。问当k=0一直到k=n的时候分别需要消耗的代价的最小值。
分析:
开始想的是贪心,然后倒着将点一个个加上去,后来发现这样不行,因为k=i不一定是在k=i+1的方案上加一个点。
后面考虑dp,dp的转移方程其实挺容易想到的,dp[i][j][k]每一位分别代表的是当前节点的编号,以这个节点为根的子树中消去的节点个数,以及当前节点是否选择。那么转移的方程即为
$$dp[i][j][0]=dp[i][j-k][0]+min{dp[son][k][0], dp[i][j-k][1]} dp[i][j][1]=dp[i][j-k][1]+min{dp[son][k][0], dp[son][k][1]+hp[son]}$$ 不过我们会想到:枚举i,j,k,那么算法的复杂度会达到O(n³),这样是过不了的。但是可以这样考虑,更新x节点的dp数组的时候,假设以它的儿子为根的子树的大小分别为s1,s2...sn,那么合并的计算次数为 $$s_1+s_1s_2+(s_1+s_2)s_3+...+(s_1+s_2+...+s_{n-1})s_n=\sum_{i=1}^n\sum_{j=1 j!=i}^ns_is_j$$ 所以算下来,对于每一个节点来说,都会与其它的节点有一次的合并操作,总共的复杂度就应该是O(n²)
代码:
1 const int N = 2010, M = 100010; 2 int head[N], num = 0; 3 struct edge 4 { 5 int nxt, to, val; 6 }; 7 edge e[M]; 8 void add_edge(int x, int y, int z) 9 { 10 e[++ num].nxt = head[x]; 11 head[x] = num; 12 e[num].to = y; 13 e[num].val = z; 14 } 15 const long long inf = (1ll << 60) - 1; 16 int n, fa; 17 int a[2010], size[2010]; 18 long long dp[2010][2010][2]; 19 long long temp[2010]; 20 21 void dfs(int x) 22 { 23 for (int i = 0; i <= n; i ++) 24 dp[x][i][0] = dp[x][i][1] = inf; 25 dp[x][0][1] = a[x]; 26 dp[x][1][0] = 0; 27 size[x] = 1; 28 for (int i = head[x]; i; i = e[i].nxt) 29 { 30 int to = e[i].to; 31 dfs(to); 32 for (int j = 0; j <= size[x] + size[to]; j ++) 33 temp[j] = inf; 34 for (int j = 0; j <= size[x]; j ++) 35 for (int k = 0; k <= size[to]; k ++) 36 temp[j + k] = min(temp[j + k], dp[x][j][0] + min(dp[to][k][0], dp[to][k][1])); 37 for (int j = 0; j <= size[x] + size[to]; j ++) 38 { 39 dp[x][j][0] = temp[j]; 40 temp[j] = inf; 41 } 42 for (int j = 0; j <= size[x]; j ++) 43 for (int k = 0; k <= size[to]; k ++) 44 temp[j + k] = min(temp[j + k], dp[x][j][1] + min(dp[to][k][0], dp[to][k][1] + a[to])); 45 for (int j = 0; j <= size[x] + size[to]; j ++) 46 { 47 dp[x][j][1] = temp[j]; 48 temp[j] = inf; 49 } 50 size[x] += size[to]; 51 } 52 } 53 54 int main() 55 { 56 int T = 1; 57 T = read(); 58 while (T --) 59 { 60 n = read(); 61 for (int i = 2; i <= n; i ++) 62 { 63 fa = read(); 64 add_edge(fa, i, 1); 65 } 66 for (int i = 1; i <= n; i ++) 67 a[i] = read(); 68 dfs(1); 69 for (int i = 0; i <= n; i ++) 70 printf ("%lld ", min(dp[1][i][0], dp[1][i][1])); 71 printf ("\n"); 72 for (int i = 1; i <= n; i ++) 73 head[i] = 0; 74 num = 0; 75 } 76 return 0; 77 }
浙公网安备 33010602011771号