[SOJ #498]隔膜(2019-10-30考试)/[POJ2152]Fire

题目大意:有一棵$n$个点的带边权树,第$i$个点有两个值$w_i,d_i$,表示在这个点做标记的代价为$w_i$,且这个点距离$d_i$以内至少要有一个点被标记,为最小代价。$nleqslant6000$

题解:记$f[i][j]$表示以$i$为根的子树全部满足条件,且第$i$个点是由于$j$被标记导致的,$g[i]$表示以$i$为根的子树全部满足条件的代价。$f[i][j]=w_i+minlimits_{vin son[u]}{f[v][j]-w_i,g[v]}$

卡点:

C++ Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn = 6e3 + 10, inf = 0x3f3f3f3f;

int n, w[maxn], d[maxn];
int head[maxn], cnt;
struct Edge {
	int to, nxt, w;
} e[maxn << 1];
void addedge(int a, int b, int c) {
	e[++cnt] = (Edge) { b, head[a], c }; head[a] = cnt;
	e[++cnt] = (Edge) { a, head[b], c }; head[b] = cnt;
}

int dis[maxn][maxn], f[maxn][maxn], g[maxn];
void dfs0(int *dis, int u, int fa = 0) {
	for (int i = head[u], v; i; i = e[i].nxt) {
		v = e[i].to;
		if (v != fa) {
			dis[v] = dis[u] + e[i].w;
			dfs0(dis, v, u);
		}
	}
}

void dfs(int u, int fa = 0) {
	for (int i = head[u]; i; i = e[i].nxt)
		if (e[i].to != fa) dfs(e[i].to, u);
	for (int i = 1; i <= n; ++i) if (dis[u][i] <= d[u]) {
		f[u][i] = w[i];
		for (int j = head[u], v; j; j = e[j].nxt) {
			v = e[j].to;
			if (v != fa) f[u][i] += std::min(f[v][i] - w[i], g[v]);
		}
		g[u] = std::min(g[u], f[u][i]);
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> n;
	for (int i = 1; i <= n; ++i) std::cin >> w[i];
	for (int i = 1; i <= n; ++i) std::cin >> d[i];
	for (int i = 1, a, b, c; i < n; ++i) {
		std::cin >> a >> b >> c;
		addedge(a, b, c);
	}
	for (int i = 1; i <= n; ++i) dfs0(dis[i], i);
	memset(f, 0x3f, sizeof f), memset(g, 0x3f, sizeof g);
	dfs(1); std::cout << g[1] << '
';
	return 0;
}

  

原文地址:https://www.cnblogs.com/Memory-of-winter/p/11769214.html