2020牛客多校9:B. Groundhog and Apple Tree(树形DP + 分类讨论 + 贪心)

2020牛客多校9:B. Groundhog and Apple Tree(树形DP + 分类讨论 + 贪心)_第1张图片


题目大意:有一棵苹果树,每个节点有一个苹果,吃掉 u u u 点的苹果能获得 a u a_u au点 HP,经过第 i i i 条边需要消耗 w i w_i wi HP,在原地等待一秒可以获得 1 1 1 HP,每条边只能经过两次,问从1号节点出发吃掉所有苹果最少需要等待多少秒。

分析:首先在某个点一次性把所需要的HP等够是显然正确的,根据每条边只能经过两次,解的形式一定是先吃掉某棵子树,再回到根,再吃其它子树,最优解是一个吃的顺序的问题,而吃掉子树是一个子问题,考虑用树形 dp。

d p [ u ] dp[u] dp[u] 表示以 u u u 为根的答案, s u m [ u ] sum[u] sum[u] 表示吃完 u u u 的子树后 HP 的变化。

s u m [ u ] sum[u] sum[u] 转移方程: s u m [ u ] = ∑ v ∈ s o n ( u ) ( s u m [ v ] − 2 ∗ w v ) sum[u] = \sum_{v\in son(u)}(sum[v] - 2 * w_v) sum[u]=vson(u)(sum[v]2wv)
d p [ u ] dp[u] dp[u] 的转移要考虑子树的遍历顺序。

对于第 i i i 棵子树,进入到这棵子树需要 a i = d p [ i ] + w i a_i = dp[i] + w_i ai=dp[i]+wi,从他回到 u u u 后 得到的 HP 为 b i = d p [ i ] + s u m [ i ] − w i b_i=dp[i]+sum[i]-w_i bi=dp[i]+sum[i]wi

对遍历顺序进行分类讨论,简化问题,考虑只有两棵子树时的遍历顺序:

先遍历 b i ≥ a i b_i \geq a_i biai 的子树更优:

设有 a i ≤ b i , a j > b j a_i \leq b_i,a_j > b_j aibi,aj>bj,分别讨论先 i i i 和先 j j j 的答案:
1、若 a i ≥ a j a_i \geq a_j aiaj,则两种情况答案分别为: a i a_i ai, a j + a i − b j a_j + a_i - b_j aj+aibj
2、若 a i < a j a_i < a_j ai<aj,则两种情况答案分别为: a i + m a x ( a j − b i , 0 ) a_i + max(a_j - b_i,0) ai+max(ajbi,0) a j + m a x ( a i − b j , 0 a_j + max(a_i - b_j,0 aj+max(aibj,0,画一下发现先 i i i 答案至多为 a j a_j aj,而先 j j j 答案至少为 a j a_j aj

若均满足 a < b aa<b
设有 a i ≤ b i , a j ≤ b j a_i \leq b_i, a_j \leq b_j aibi,ajbj,设 a i < a j a_i < a_j ai<aj
两种情况答案分别为: a i + m a x ( a j − b i , 0 ) a_i + max(a_j - b_i,0) ai+max(ajbi,0) a j a_j aj,由于 b i ≥ a i b_i \geq a_i biai,显然有 a i + m a x ( a j − b i , 0 ) ≤ a j a_i + max(a_j - b_i,0) \leq a_j ai+max(ajbi,0)aj

若均满足 a > b a>b a>b
设有 a i > b i , a j > b j a_i > b_i, a_j > b_j ai>bi,aj>bj
两种情况分别为: a i + m a x ( a j − b i , 0 ) a_i + max(a_j - b_i,0) ai+max(ajbi,0) a j + m a x ( a i − b j , 0 ) a_j + max(a_i - b_j,0) aj+max(aibj,0),在纸上画一下可以发现先遍历 b b b 更大的子树更优。


代码:

#include
using namespace std;
#define pii pair
#define fir first
#define sec second
typedef long long ll;
const int maxn = 1e5 + 10;
vector<pii> g[maxn];
int t, n, a[maxn];
ll dp[maxn], sum[maxn];
struct node {
     
	ll in, out;
	node() {
     }
	node(ll i,ll j) {
     
		in = i, out = j;
	}
};
bool cmp1(node x,node y) {
     
	return x.in < y.in;
}
bool cmp2(node x,node y) {
     
	return x.out > y.out;
}
void dfs(int u,int fa) {
     
	dp[u] = 0, sum[u] = a[u];
	vector<node> x, y;
	for (auto it : g[u]) {
     
		int v = it.fir, w = it.sec;
		if (v == fa) continue;
		dfs(v,u);
		sum[u] += sum[v] - 2ll * w;
		node t;
		if (w <= dp[v] + sum[v])
			t = node(w + dp[v],dp[v] + sum[v] - w);
		else
			t = node(2ll * w - sum[v],0);
		if (t.in <= t.out)
			x.push_back(t);
		else
			y.push_back(t);
	}
	sort(x.begin(),x.end(),cmp1);
	sort(y.begin(),y.end(),cmp2);
	ll cur = a[u];
	for (auto it : x) {
     
		if (it.in > cur) {
     
			dp[u] += it.in - cur;
			cur = it.in;
		}
		cur = cur - it.in + it.out;
	}
	for (auto it : y) {
     
		if (it.in > cur) {
     
			dp[u] += it.in - cur;
			cur = it.in;
		}
		cur = cur - it.in + it.out;
	}
}
int main() {
     
	scanf("%d",&t);
	while (t--) {
     
		scanf("%d",&n);
		for (int i = 1; i <= n; i++)
			g[i].clear();
		for (int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		for (int i = 1; i < n; i++) {
     
			int u, v, w;
			scanf("%d%d%d",&u,&v,&w);
			g[u].push_back(pii(v,w));
			g[v].push_back(pii(u,w));
		}
		dfs(1,0);
		printf("%lld\n",dp[1]);
	}
	return 0;
}

你可能感兴趣的:(树形dp,贪心,分类讨论,思维)