HDU 4003 Find Metal Mineral
这个题是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 }
二、
还有一种更为巧妙的做法,就是每次从起点出发找到一条最长的路径,记录下路径的长度,然后把这条路径上边的权值变为相反数。注意,已经变为负权的边就不再变回去了。
这样找K次最长的路径,然后加起来就是可以最多少走多少路。
为什么要把权值变成负的,因为你第二次第三次再走这条路的时候,就表示少走了负的权值,也就是多走了。如果出现所有的路都是负权的情况,那么树中最长路的路径就是0,也就是机器人原地不动。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 10000 + 10; 9 10 struct Edge 11 { 12 int u, v, w; 13 Edge(int u, int v, int w):u(u), v(v), w(w) {} 14 }; 15 16 vector<Edge> edges; 17 vector<int> G[maxn]; 18 19 void AddEdge(int u, int v, int w) 20 { 21 edges.push_back(Edge(u, v, w)); 22 edges.push_back(Edge(v, u, w)); 23 int m = edges.size(); 24 G[u].push_back(m - 2); 25 G[v].push_back(m - 1); 26 } 27 28 int n, s, k; 29 30 int len, id; 31 int pre[maxn]; 32 33 void dfs(int u, int fa, int d) 34 { 35 if(d > len) { len = d; id = u; } 36 for(int i = 0; i < G[u].size(); i++) 37 { 38 Edge& e = edges[G[u][i]]; 39 int v = e.v; 40 if(v == fa) continue; 41 pre[v] = G[u][i]; 42 dfs(v, u, d + e.w); 43 } 44 } 45 46 int main() 47 { 48 while(scanf("%d%d%d", &n, &s, &k) == 3) 49 { 50 edges.clear(); 51 int tot = 0; 52 for(int i = 1; i <= n; i++) G[i].clear(); 53 for(int i = 1; i < n; i++) 54 { 55 int u, v, w; scanf("%d%d%d", &u, &v, &w); 56 tot += w; 57 AddEdge(u, v, w); 58 } 59 tot <<= 1; 60 61 int ans = 0; 62 for(int i = 0; i < k; i++) 63 { 64 len = 0, id = s; 65 pre[s] = -1; 66 dfs(s, 0, 0); 67 if(id == s) continue; 68 ans += len; 69 for(int u = id; u != s; u = edges[pre[u]].u) 70 { 71 int& w = edges[pre[u]].w; 72 if(w > 0) w = -w; 73 } 74 } 75 76 printf("%d\n", tot - ans); 77 } 78 79 return 0; 80 }