原题链接:P5521 [yLOI2019] 梅深不见冬 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:一棵树,要在每个节点上都放过一次梅花,每个节点满足当其子节点被放满了才可以放梅花,每个节点要放的梅花数量是其入度的权重值,梅花可以回收,每次只能到父节点或者没有到达过的一个子节点放梅花,求每个节点放梅花的最小梅花数量
思路
首先这是DFS搜索顺序
然而不同的子节点搜索顺序会有不同的结果
要怎么对子节点排序就是最大的问题
贪心就是要写式子推导证明(理解)而不是光发呆
分类讨论:
- 如果大,那么就有,展开w,可以得到 ,此时先选j的情况有 都满足小于
- 如果大,那么就有,对于先选j,有两种都要大于它,则有 且 得到不可以选第一个,必须满足 化简得到 且 发现只要满足 就成立,也就是当满足的时候,该顺序就是最佳顺序
那就是简单的排序和实现
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
vector<vector<int>> son(N);
vector<int> w(N);
vector<int> ans(N);
int n;
bool cmp(int x, int y)
{
return ans[x] - w[x] > ans[y] - w[y];
}
void dfs(int x)
{
for(auto t: son[x])
{
//一直找到叶子节点,从下往上更新
dfs(t);
}
//排序
sort(son[x].begin(), son[x].end(), cmp);
int res = 0;
for(auto t: son[x])
{
//当前还有剩,且够用
if(res > ans[t])
{
res -= w[t];
}
else
{
//不够用了,就更新ans,更新后res就等于新子节点剩下的点
ans[x] += ans[t] - res;
res = ans[t] - w[t];
}
}
// 根节点自身权重
ans[x] += max(0, w[x] - res);
}
int main()
{
cin >> n;
for(int i = 2; i <= n ; i ++)
{
int x;
cin >> x;
son[x].push_back(i);
}
for(int i = 1; i <= n; i ++)
{
cin >> w[i];
}
dfs(1);
for(int i = 1; i <= n; i ++)
{
cout << ans[i] << ' ';
}
cout << endl;
return 0;
}