链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
青青草原上,🐏🐺大战已经到了白热化阶段,狼族刚刚建造出一堆导弹,有烤羊型和蒸羊型两种型号,两种型号造价分别为a盘豆腐羊羔,b盘豆腐羊羔。通过情报狼可以知道,羊村里有n个据点,且据点之间的道路呈树状。经过试爆得知,烤羊型导弹轰击在u据点可以将与 u据点 相连的道路摧毁,蒸羊型导弹轰击在u据点可以将与 u据点 以及 与u据点相邻的据点 相连的道路摧毁,由于羊村据点有极强的防御,所以这两种导弹都不能摧毁羊村据点。灰太狼准备用这些导弹来轰击羊村,摧毁所有的道路,让各据点之间无法联系,由于豆腐羊羔有限,灰太狼想要知道完成这次任务需要的豆腐羊羔最小是多少。
烤羊型导弹摧毁范围:
蒸羊型导弹摧毁范围:
解题思路:
本题是一道较难的树形dp,由题得知,对每个点我们可以投放两种导弹,烤羊型和蒸羊型,设为A型及B型,A型需a的花费,可以摧毁与当前点相连的道路,B型需b的花费,可以摧毁与当前点相连的道路,以及与 与当前点相连的点 相连的道路。
- dp(u,0)表示以点u为根的子树下的边全部被覆盖,且没有向u节点上方覆盖。换句话说也就是u不安装装置,而u和儿子的连边被儿子覆盖,而且儿子不能用B,那么儿子要么用A ,要么就是孙子用了B使得u和儿子的连边被覆盖,那么状态的转移:dp(u, 0) = sum{min(dp(v,0),dp(v,1))}
- dp(u,1)表示以点u为根的子树下的边全被覆盖,而且向上覆盖了一条边,其实也就是覆盖了u和其父亲相连的边。换句话说,如果想向上覆盖一条边,要么u用了A,要么儿子用了B,因此需要比较是在儿子用B划算还是在u用A划算。但是不要忘了u的子树全被覆盖这个条件,可以从儿子的前三个状态转移。因此状态转移方程为dp(u,1)=sum{min(dp(v,0),dp(v,1),dp(v,2))+min(c1,min( dp(v,2)-min(dp(v,0),dp(v,1),dp(v,2)))}这里之所以后面需要减去一部分,是因为如果一个儿子用了B那么就不需要从其他两个状态更新了,也就是说这里多加了用B的那个儿子的三种转移状态的其中一种,因此要减去
- dp(u,2)表示以点u为根的子树下的边全被覆盖,而且向上覆盖了两条边,其实也就是一条覆盖了u和其父亲相连的边,另外一条可以覆盖父亲和祖父的,也可以覆盖父亲和兄弟的。那么显然儿子不管是什么状态都不能向上覆盖两条边,这个时候只能是在u用B,但是u用了B之后儿子和孙子都可以不装,那么也就是能从儿子的四个状态转移而来
- d(u,3)表示以点u的儿子为根的子树下的边全被覆盖,而且u和儿子的连边已经被父亲覆盖。这个状态相当于是第三个状态的补充,因为两个相邻节点都不装的状态是没有的。虽然u和儿子的边已经被覆盖,但是对于u的儿子来说还是能从前三种状态转移而来的,第四种无法转移是因为已经规定u不放装置。那么状态转移方程为dp(u,3)=sum{ min(dp(v,0),dp(v,1),dp(v,2))}
这题是我在UVA上碰到的一个树形dp,觉得挺有意思的,就搬过来了。
洛谷大佬的题解写得很好。
洛谷题目链接:保卫Zonk Protecting Zonk - 洛谷
题解参考:UVA - 12093 Protecting Zonk(树形DP经典难题)_Happig丶的博客-CSDN博客
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 10;
const int M = 2e6 + 10;
const ll Inf = 1e9;
int n;
int vala, valb;
int e[M], ne[M], h[N], idx;
int dp[N][4];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa) {
dp[u][1] = vala;
dp[u][2] = valb;
int c = Inf;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa)continue;
dfs(v, u);
dp[u][0] += min(dp[v][1], dp[v][2]);
int tmp = min(dp[v][0], min(dp[v][1], dp[v][2]));
dp[u][1] += tmp;
dp[u][2] += min(tmp, dp[v][3]);
dp[u][3] += tmp;
c = min(c, dp[v][2] - tmp);
}
dp[u][1] = min(dp[u][1], dp[u][1] - vala + c);
}
int main() {
scanf("%d %d %d", &n, &vala, &valb);
memset(h, -1, sizeof(h));
memset(dp, 0, sizeof(dp));
idx = 0;
for (int i = 1; i < n; i++) {
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1, 0);
printf("%d\n", min(dp[1][0], min(dp[1][1], dp[1][2])));
}