洛谷链接:
思路:
考虑贪心思路,对于每个点,要尽量往下吃尽可能多的海狸,所以贪心地挑选能吃尽可能多海狸的子结点,直到本节点剩余海狸数为0。用dfs计算每个子树能吃到的海狸数。
如果此时本节点还有海狸剩余,那么还能额外吃2*min(rest, sonRest)个海狸,其中rest表示本节点剩余海狸,sonRest表示所有子结点剩余海狸数之和。(即往下一格然后立刻返回,每次吃两个)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> P;
const int maxn = 1e5+5;
int n, a[maxn], root;
vector<int> g[maxn];
bool cmp(int a,int b) {return a>b;}
P dfs(int u, int fa){ //返回 pair{eat, rest}
vector<int> kids; //记录每个子结点能吃多少
int sonRest = 0;
for(int v : g[u]){
if(v==fa) continue;
P son = dfs(v, u); //儿子传回信息
kids.push_back(son.first);
sonRest += son.second;
}
if(kids.empty()) return {0,a[u]-1}; //一个也没吃,次数-1(刚来就吃了一个)
int eat = 0, rest = a[u]-1; //本子树内吃的海狸数,本节点剩下的海狸
sort(kids.begin(), kids.end(), cmp); //排序,多的排前面
for(int i=0; i<kids.size() && rest>0; i++, rest--){
//遍历所有孩子,同时要保证本节点有rest海狸
eat += kids[i]+2; // +2表示这条边
}
eat += 2*min(sonRest, rest);
rest -= min(sonRest, rest);
return {eat, rest};
}
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for(int i=1; i<=n; i++) cin >> a[i];
for(int i=1; i<=n-1; i++){
int x, y; cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
cin >> root;
a[root]++; //特殊处理,根结点开始不吃
cout << dfs(root, 0).first;
}