这个题是POJ1849的加强版。

 

先说一个很重要的结论,下面两种方法都是从这个结论出发的。

一个人从起点遍历一颗树,如果最终要回到起点,走过的最小权值就是整棵树的权值的2倍

而且K个人的情况也是如此,大不了只有一个人走,其他K-1个人待着不动就行了。

 

而题目中说了这些人不比回到原点,所以就想办法考虑哪些多走的路程,最后用整棵树权值2倍减去就好了。

 

一、

多数人的做法是分组背包,推荐这篇博客

里面的状态的定义并不复杂,和网上那些千篇一律的做法比,思路很新颖。

f(i, j)表示i为根的子树有j个机器人出发,遍历这棵子树可以少走多少路。

最终答案为sum - f(S, K)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <vector>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const int maxn = 10000 + 10;
 9 const int maxk = 12;
10 
11 int n, s, k;
12 
13 vector<int> G[maxn], C[maxn];
14 
15 int f[maxn][maxk];
16 
17 void DP(int u, int fa)
18 {
19     for(int i = 0; i < G[u].size(); i++)
20     {
21         int v = G[u][i], w = C[u][i];
22         if(v == fa) continue;
23         DP(v, u);
24         for(int i = k; i >= 1; i--)
25             for(int j = 1; j <= i; j++)
26                 f[u][i] = max(f[u][i], f[u][i-j] + f[v][j] + (2 - j) * w);
27     }
28 }
29 
30 int main()
31 {
32     while(scanf("%d%d%d", &n, &s, &k) == 3)
33     {
34         for(int i = 1; i <= n; i++) { G[i].clear(); C[i].clear(); }
35 
36         int sum = 0;
37         for(int i = 1; i < n; i++)
38         {
39             int u, v, w; scanf("%d%d%d", &u, &v, &w);
40             sum += w;
41             G[u].push_back(v); C[u].push_back(w);
42             G[v].push_back(u); C[v].push_back(w);
43         }
44         sum <<= 1;
45 
46         memset(f, 0, sizeof(f));
47         DP(s, 0);
48         printf("%d\n", sum - f[s][k]);
49     }
50 
51     return 0;
52 }
代码君

相关文章: