hdu 5290 Bombing plan(树形dp)

179 篇文章 0 订阅

题目链接:hdu 5290 Bombing plan


dpDestroy[u][i]表示以u为根节点的子树全部被摧毁,并且向上还可以破坏到距离u为i的城市;dpSafe[u][i]表示以u为根节点的子树中有距离u深度为i的城市还未被破坏。

dpDestroy[u][i] = dpDestroy[v][i+1] + sum{ min(dpDestroy[k][j], dpSafe[k][j])(j≤i)| k为除了v以外的子节点}

dpSafe[u][i] = dpSafe[v][i-1] + sum{ min(dpSafe[k][j], dpDestroy[k][j]) (j≤i)| k为除了v以外的子节点}

然后在转移一下u节点放炸弹的情况,这样的直接做的复杂度是o(n * w * w)

对于每个节点,可以维护一个前缀最小值,加速查询,minDestroy[u][i] = min{ dpDestroy[u][j] (j≤i)}


#pragma comment(linker, "/STACK:102400000,102400000")  
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 100;
const int inf = 0x3f3f3f3f;

int N, W[maxn], dpDestroy[maxn][maxm + 5], dpSafe[maxn][maxm + 5];
int minDestroy[maxn][maxm + 5], minSafe[maxn][maxm + 5];
vector<int> G[maxn];

void init () {
	for (int i = 1; i <= N; i++) {
		scanf("%d", &W[i]);
		G[i].clear();
	}

	int u, v;
	for (int i = 1; i < N; i++) {
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
}

void dfs (int u, int f) {
	for (int i = 0; i <= maxm; i++)
		dpDestroy[u][i] = dpSafe[u][i] = N;


	int tmp = 0;
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		if (v == f)
			continue;

		dfs(v, u);
		tmp += min(minDestroy[v][W[u]], (W[u] ? minSafe[v][W[u]-1] : inf));
	}
	dpDestroy[u][W[u]] = tmp + 1;

	for (int i = 0; i < maxm; i++) {
		int p = 0, q = 0;
		for (int j = 0; j < G[u].size(); j++) {
			int v = G[u][j];
			if (v == f)
				continue;
			p += min(minDestroy[v][i + 1], (i == 0 ? inf : minSafe[v][i-1]));
			q += min(minDestroy[v][i], (i == 0 ? inf : minSafe[v][i-1]));
		}

		for (int j = 0; j < G[u].size(); j++) {
			int v = G[u][j];
			if (v == f)
				continue;
			//dpDestroy[u][i] = min(dpDestroy[u][i], p+dpDestroy[v][i+1]-min(minDestroy[v][i+1], minSafe[v][i]));
			if (i)  {
				dpDestroy[u][i] = min(dpDestroy[u][i], p+dpDestroy[v][i+1]-min(minDestroy[v][i+1], minSafe[v][i-1]));
				dpSafe[u][i] = min(dpSafe[u][i], q + dpSafe[v][i-1] - min(minDestroy[v][i], minSafe[v][i-1]));
			} else {
				dpDestroy[u][i] = min(dpDestroy[u][i], p+dpDestroy[v][i+1]-min(minDestroy[v][i+1], inf));
				dpSafe[u][i] = min(dpSafe[u][i], q);
			}
		}
	}

	if (G[u].size() == 1)
		dpSafe[u][0] = 0;


	minDestroy[u][0] = dpDestroy[u][0];
	minSafe[u][0] = dpSafe[u][0];
	for (int i = 1; i <= maxm; i++) {
		minDestroy[u][i] = min(minDestroy[u][i-1], dpDestroy[u][i]);
		minSafe[u][i] = min(minSafe[u][i-1], dpSafe[u][i]);
	}
}

int main () {
	while (scanf("%d", &N) == 1) {
		init();
		dfs(1, 0);
		printf("%d\n", minDestroy[1][maxm]);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值