长沙学院20级训练赛 F-轰击羊村

链接:登录—专业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])));
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值