[ZPG TEST 108] blockenemy【树形dp】
T3:blockenemy
blockenemy.pas/in/out 128M 1s
你在玩电子游戏的时候遇到了麻烦。。。。。。
你玩的游戏是在一个虚拟的城市里进行,这个城市里有n个点,都从0~n-1编了号,每两个点之间有且仅有一条路径。现在,你的敌人到这个城市来踩点了!!!为了阻止他们更好的踩点,
你决定切断他们所有踩点人员的联系,使他们孤军作战,然后在各个击破。但是这就要切断某些街道,而你每切断一条路,市民就会产生相对的不满值,不满值越大,城市的和谐度就越小。所以你现在需要知道为了使踩点人员所在的点两两之间不联通所切断的边产生的最小不满值是多少?
Input
第一行一个数:n n<=50
以下n-1行,每行3个数 a,b,c 表示a点和b点之间有条路,切断这条路的不满值为c
以下若干行 每行一个数,表示踩点人员的位置
Output
一个数,最小不满值
Sample Input
5
1 0 1
1 2 2
0 3 3
4 0 4
3
2
4
Sample Output
4
据说这道题有许多做法,直接复制题面的第一句话到百度里搜索,就会有别人的做法,我的做法是std的,想了好久才搞定。
对于每个节点i,令f(i, 0)表示以i为根的子树中,全部敌人两两不连通,且全部都无法到达点i的最小值;f(i, 1)表示以i为根的子树中,全部敌人两两不连通,但有1个敌人可以到底点i的最小值。状态设计好了,接下来就是分两种情况转移:
情况①:若i这个点有敌人。显然,f(i, 0)肯定为inf了,因为不可能做到“全部都无法到达点i”。f(i, 1) = sigma(min(f(j, 1) + w(i, j), f(j, 0))),其中j属于i的儿子。
情况②:若i这个点没敌人。那么
f(i, 0) = sigma(min(f(j, 1) + w(i, j), f(j, 0))),其中j属于i的儿子。
f(i, 1)的转移有那么一丢丢难想。f(i, 1)一定实在f(i, 0)的基础上,使得某个儿子从不能到达i,变成能到达i,即对于儿子j,对该状态的贡献由min(f(j, 1) + w, f(j, 0))变为f(j, 1).如果这样的话,f(i, 1)就会在f(i, 0)的基础上减去min(f(j, 1) + w, f(j, 0)) - f(j, 1),那么这个值越大,f(i, 1)就会越小。
#include <cstdio> #include <cstdlib> #include <cstring> const int maxn = 55; int n, t1, t2, t3, root, f[maxn][2]; bool book[maxn]; struct graph { int head[maxn], to[maxn << 1], next[maxn << 1], w[maxn << 1], lb; graph(void) { memset(head, -1, sizeof head); memset(next, -1, sizeof next); lb = 0; } void ist(int aa, int ss, int ww) { to[lb] = ss; w[lb] = ww; next[lb] = head[aa]; head[aa] = lb; ++lb; } } g; inline int minn(int aa, int ss) { return aa < ss? aa: ss; } inline int maxx(int aa, int ss) { return aa > ss? aa: ss; } void dfs(int r, int p) { if (book[r]) { f[r][0] = 0x3c3c3c3c; for (int j = g.head[r]; j != -1; j = g.next[j]) { if (g.to[j] != p) { dfs(g.to[j], r); f[r][1] += minn(f[g.to[j]][1] + g.w[j], f[g.to[j]][0]); } } } else { int mx = 0, mn; for (int j = g.head[r]; j != -1; j = g.next[j]) { if (g.to[j] != p) { dfs(g.to[j], r); mn = minn(f[g.to[j]][0], f[g.to[j]][1] + g.w[j]); f[r][0] += mn; mx = maxx(mx, mn - f[g.to[j]][1]); } } f[r][1] = f[r][0] - mx; } } int main(void) { freopen("blockenemy.in", "r", stdin); freopen("blockenemy.out", "w", stdout); scanf("%d", &n); unsigned seed; for (int i = 1; i < n; ++i) { scanf("%d%d%d", &t1, &t2, &t3); seed += t1 + t2 + t3; g.ist(t1, t2, t3); g.ist(t2, t1, t3); } while (scanf("%d", &t1) != EOF) { book[t1] = true; seed += t1; } srand(seed); root = rand() % n; dfs(root, -1); printf("%d\n", f[root][1]); return 0; }