HDU4003:Find Metal Mineral
——求K个机器人从同一点出发,遍历所有点所需的最小花费
——树形DP,分组背包
——url:http://acm.hdu.edu.cn/showproblem.php?pid=4003
————————————————————————————————————————————————
【转载】:
dp[i][j]表示对于以i结点为根结点的子树,放j个机器人所需要的权值和。
当j=0时表示放了一个机器人下去,遍历完结点后又回到i结点了。状态转移方程类似背包
如果最终的状态中以i为根结点的树中有j(j>0)个机器人,那么不可能有别的机器人r到了这棵树后又跑到别的树中去
因为那样的话,一定会比j中的某一个到达i后跑与r相同的路径再回到i,再接着跑它的路径要差(多了一条i回去的边)
这样的话,如果最后以i为根结点的树中没有机器人,那么只可能是派一个机器人下去遍历完后再回来
————————————————————————————————————————————————
状态转移,使用的“分组背包”思想。
使用一维数组的“分组背包”伪代码如下:
for 所有的组i
for v=V..0
for 所有的k属于组i
f[v]=max{f[v],f[v-c[k]]+w[k]}
——————
可以这么理解:
对于每个根节点root,有个容量为K的背包
如果它有i个儿子,那么就有i组物品,价值分别为dp[son][0],dp[son][1].....dp[son][k] ,这些物品的重量分别为0,1,.....k
现在要求从每组里选一个物品(且必须选一个物品)装进root的背包,使得容量不超过k的情况下价值最大。
那么这就是个分组背包的问题了。
但是这里有一个问题,就是每组必须选一个物品。
对于这个的处理,我们先将dp[son][0]放进背包,如果该组里有更好的选择,那么就会换掉这个物品,否则的话这个物品就是最好的选择。这样保证每组必定选了一个。
1 #include<stdio.h>
2 #include<memory.h>
3 #define N 10010
4 #define oo 0x7ffffff
5 int head[N], nxt[2 * N], ev[2*N], ew[2*N], dp[N][11];
6 int num, n, s, k;
7 int min(int a, int b)
8 {
9 return a > b ? b : a;
10 }
11 void addedge(int u, int v, int w)
12 {
13 nxt[++num] = head[u];
14 head[u] = num;
15 ev[num] = v;
16 ew[num] = w;
17 nxt[++num] = head[v];
18 head[v] = num;
19 ev[num] = u;
20 ew[num] = w;
21 }
22 void dfs(int father, int u)
23 {
24 int i, j, l, v;
25 bool vis = false;
26 for (i = head[u]; ~i; i = nxt[i])
27 {
28 v = ev[i];
29 if (v != father)
30 {
31 dfs(u, v);
32 vis = true;
33 }
34 }
35 if (!vis)
36 return;
37 for (i = head[u]; ~i; i = nxt[i]) //分组背包
38 {
39 v = ev[i];
40 if (v == father)
41 continue;
42 for (l = k; l >= 0; l--)
43 {
44 dp[u][l] += dp[v][0] + 2 * ew[i]; //先把DP[V][0]放进背包。
45 for (j = 1; j <= l; j++)
46 dp[u][l] = min(dp[u][l], dp[u][l - j] + dp[v][j] + j * ew[i]);
47 }
48 }
49 }
50 int main()
51 {
52 int i, j, u, v, w;
53 while (scanf("%d%d%d", &n, &s, &k) != EOF)
54 {
55 num = -1;
56 memset(head, -1, sizeof(head));
57 memset(dp, 0, sizeof(dp));
58 for (i = 0; i < n - 1; i++)
59 {
60 scanf("%d%d%d", &u, &v, &w);
61 addedge(u, v, w);
62 }
63 dfs(s, s);
64 printf("%d\n", dp[s][k]);
65 }
66 return 0;
67 }