Give n vertices and n-1 edges (a spanning tree ). Given K points, there is a robot on these points. Finally, let you delete some edges so that any two robots cannot reach each other, and the sum of the deleted edges must be minimized. Http://acm.hdu.edu.cn/showproblem.php? PID = 1, 4313
Solution:
First, we can see two options without decision-making.
1. If the root node is a dangerous point, the Child tree cannot have a dangerous point.
DP [u] [0]: The smallest edge weight that needs to be deleted when there is no dangerous knot in the child node connected to u. If U is a dangerous node, the value is infinite.
DP [u] [1]: The smallest edge weight to be deleted when there is a dangerous knot in the child node connected to u.
Consider the subtree with u as the root node as a leaf node.
If I is a leaf node: If I is a dangerous point DP [I] [0] = inf, DP [I] [1] = 0; otherwise, DP [I] [0] = DP [I] [1] = 0;
If I is not a leaf node: If I is a dangerous point DP [I] [0] = inf, DP [I] [1] = Sigma min (DP [son] [0], DP [son] [1] + W );
Otherwise, DP [I] [0] = Sigma min (DP [son] [0], DP [son] [1] + W ), DP [I] [1] = min (DP [I] [0]-min (DP [son] [0], DP [son] [1] + W) + dp [son] [1]).
I said that I am very orz, but I can't do it. I haven't understood it for a day. I felt that I couldn't get stuck with this question, so I got stuck with the code.
#include <cstdio>#include <cstring>const int MAXN = 100005;const __int64 INF = 10000000000000000LL;__int64 dp[MAXN][2];int n, k;struct Edge{ int v, w, next;}edge[MAXN * 2];int edgeNumber, head[MAXN];bool haveMachine[MAXN];inline void addEdge(int u, int v, int w){ edge[edgeNumber].v = v; edge[edgeNumber].w = w; edge[edgeNumber].next = head[u]; head[u] = edgeNumber ++;}inline __int64 min(const __int64 &x, const __int64 &y){ return x < y ? x : y;}void dfs(int u, int father){ if(haveMachine[u]) { dp[u][0] = INF; dp[u][1] = 0; } else { dp[u][0] = 0; dp[u][1] = INF; } __int64 minS = INF; __int64 count = 0; for(int i=head[u];i!=-1;i=edge[i].next) { int v = edge[i].v; int w = edge[i].w; if(v != father) { dfs(v, u); if(haveMachine[u]) { dp[u][1] += min(dp[v][0], dp[v][1] + w); } else { dp[u][0] += min(dp[v][0], dp[v][1] + w); ++ count; if(dp[v][0] <= dp[v][1] + w) { if(minS > dp[v][1] - dp[v][0]) { minS = dp[v][1] - dp[v][0]; } } else { if(minS > - w) { minS = - w; } } } } } if(!haveMachine[u] && count) { dp[u][1] = dp[u][0] + minS; }}int main(){ int t; int x, y, z; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &k); edgeNumber = 0; memset(head, -1, sizeof(head)); for(int i=1;i<n;++i) { scanf("%d%d%d",&x,&y,&z); addEdge(x, y, z); addEdge(y, x, z); } memset(haveMachine, false, sizeof(haveMachine)); for(int i=0;i<k;++i) { scanf("%d", &x); haveMachine[x] = true; } dfs(0, -1); if(haveMachine[0]) { printf("%I64d\n", dp[0][1]); } else { printf("%I64d\n", min(dp[0][0], dp[0][1])); } } return 0;}
As a result, I finally got a little understanding. This is another code that I can understand. There are comments in it, and you will be very open when you read the comments.
Here, I would like to thank the city again. The purpose of tree DP is to only consider the son of U and U when recursion to U. In this way, I understand.
Assume U has five children, two risks. For the outside, what kind of sub-tree is safe? Remove all dangerous edges.
How can this problem be considered unsafe? Only one side is broken. (Note: For the inside of the subtree of U, it must be safe, because when u is a leaf, whether it is dangerous or not, it must be harmless to itself ).
Then you can easily figure it out.
# Include <iostream> # include <cstdio> # include <vector> # include <cstring> using namespace STD; const _ int64 INF = 1e13; const int n = 111111; __int64 DP [N] [2]; // DP [I] [0] indicates that this subtree does not need to be disconnected from the top. // DP [I] [1] indicates that it needs to be disconnected from the top Int Is [N]; vector <pair <int, int> V [N]; inline _ int64 min (_ int64 A, _ int64 B) {return a <B? A: B;} void DFS (INT U, int pre) {int Len = V [u]. size (); If (is [u]) {DP [u] [0] = inf; // This is not possible =-DP [u] [1] = 0 ;} else {DP [u] [0] = 0; DP [u] [1] = 0;} For (INT I = 0; I <Len; I ++) {int VV = V [u] [I]. first; If (VV = pre) continue; DFS (VV, u) ;__ int64 W = V [u] [I]. second; // It is so short, but you don't understand it... // DP [I] [0] indicates that this subtree does not need to be broken from the top, therefore, this subtree is safe. // DP [I] [1] indicates that it needs to be broken from the above _ int64 T1 = DP [VV] [1]; _ int64 T0 = min (t1 + W, DP [VV] [0]); // you need the minimum value of this edge or not, this is safe. If (is [u]) {DP [u] [1] + = T0; // how can it all be broken from the above? Just take the minimum value, however, the sub-tree of u should be safe. // It is impossible not to break with the above.} Else {// until this edge is related to the previous edge. // To be dangerous, or the previous U is safe, the subtree is insecure now, // or the previous U is insecure, and the subtree is safe now. DP [u] [1] = min (DP [u] [0] + T1, DP [u] [1] + T0); // to ensure security, consistency is always safe. DP [u] [0] + = T0 ;}} int main () {int t, n, k; scanf ("% d", & T ); while (t --) {scanf ("% d", & N, & K); For (INT I = 0; I <n; I ++) V [I]. clear (); For (INT I = 1; I <n; I ++) {int A, B, C; scanf ("% d ", & A, & B, & C); V [A]. push_back (make_pair (B, c); V [B]. push_back (make_pair (a, c);} memset (is, 0, sizeof (is); For (INT I = 0; I <K; I ++) {int A; scanf ("% d", & A); is [a] = 1;} DFS (0,-1 ); __int64 ans = DP [0] [0]; If (DP [0] [1] <ans) ans = DP [0] [1]; cout <ans <Endl;} return 0 ;}