原题请看这里
土拨鼠非常擅长爬树。
一天,土拨鼠来到一棵苹果树上。出于某种原因,他决定吃掉树上的所有苹果。苹果树上有 n {n} n个点,每个点上都有一个苹果。这些点由 n − 1 {n-1} n−1条边连接(所有点都被连接)。在每个边上都有一个障碍物,这需要一定的 H P HP HP才能让 G r o u n d h o g Groundhog Groundhog跳过。如果 G r o u n d h o g Groundhog Groundhog吃了 i t h {i ^ {th }} ith在树上的苹果,他可以恢复 a i H P {a_i} HP aiHP。他可以度过没有苹果的地步。土拨鼠还可以休息一个时间来恢复 1 H P {1} HP 1HP。
注意:土拨鼠的 H P HP HP不能随时为负,但可以为 0 0 0或无穷大。他只能吃一个苹果,但是每次他越过边缘时都会消耗 H P HP HP。土拨鼠没有时间跳过障碍或吃苹果。由于边缘很脆弱,因此土拨鼠最多只能穿过每个边缘两次。
现在,土拨鼠开始从树的根节点 1 {1} 1开始爬树,他的初始 H P HP HP为零。他想在经过所有点后返回到 1 {1} 1点。由于休息以恢复他的 H P HP HP非常无聊,他想问你最小的休息时间是他遍历所有要点并回到根源的时间。
第一行中有一个整数 T {T} T表示有 T {T} T组数据,每个数据集包含:
第一行中的整数 n {n} n表示苹果树上有 n {n} n个点。
下一行包含 n {n} n个整数, i t h {i ^ {th}} ith个整数 a i {a_i} ai表示可以通过食用 i t h {i ^ {th}} ith苹果来恢复的 H P HP HP。
接下来的 n − 1 {n-1} n−1行,每行包含三个整数 u i , v i , w i {u_i,v_i,w_i} ui,vi,wi,指示在 u i {u_i} ui和 v i {v_i} vi之间存在一条边,障碍物消耗 w i H P {w_i} HP wiHP。
对于每个数据集,输出一个整数,表示土拨鼠休息的最短时间。
1
5
4 2 1 5 7
1 2 4
1 3 5
4 2 9
5 2 3
23
He can traverse in the order of 1 → 3 → 1 → 2 → 5 → 2 → 4 → 2 → 1.
1⩽T⩽1000,1⩽n⩽10^5,∑n⩽10^6,0⩽ai,wi<2^31
树形 d p dp dp
观察题目意思,我们发现遍历整棵树其实就是从根节点出发遍历每一个子树,最后回到根节点,对于根节点的每个子树其实是一样的,没有后效性,就可以用动态规划来做,在树上跑动规,就是树形 d p dp dp啦。
分析题意我们可以想到:访问 i i i子树所需的总 H P a i HPa_i HPai是个定值,所以我们只需要考虑访问顺序所带来的影响
在遍历每个节点时,我们需要用到一个贪心思想:
所以我们只要排一下序再按以上策略跑树形 d p dp dp即可
vector版:
#include
#define ll long long
using namespace std;
const int MAXN=1e6+5;
struct node{
ll first,second;
node(){}
node(ll _fi,ll _se){
first=_fi;
second=_se;
}
}dp[MAXN];
bool cmp(node x,node y){
if(x.second>=x.first){
if(y.second<y.first) return true;
return x.first<y.first;
}
else{
if(y.second>=y.first) return false;
return x.second>y.second;
}
}//贪心排序
vector<node> vec[MAXN];
int t,n,a[MAXN];
ll minn,now;
void get_ans(int pos,int fa){
vector<node> tor;
for(int i=0;i<vec[pos].size();++i){
node v=vec[pos][i];
if(v.first==fa) continue;
get_ans(v.first,pos);
dp[v.first].first+=v.second;
dp[v.first].second-=v.second;
if(dp[v.first].second<0)
dp[v.first]=node(dp[v.first].first-dp[v.first].second,0);
tor.push_back(dp[v.first]);
}
sort(tor.begin(),tor.end(),cmp);
minn=now=a[pos];
for(int i=0;i<tor.size();++i){
node v=tor[i];
minn=min(minn,now-v.first);
now+=v.second-v.first;
}
if(minn>=0) dp[pos]=node(0,now);
else dp[pos]=node(-minn,now-minn);
}
ll u,v,dis;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
vec[i].clear();
}
for(int i=1;i<n;++i){
scanf("%lld%lld%lld",&u,&v,&dis);
vec[u].push_back(node(v,dis));
vec[v].push_back(node(u,dis));
}
get_ans(1,-1);
printf("%lld\n",dp[1].first);
}
}
pair版:
#include
#define ll long long
using namespace std;
const int MAXN=1e6+5;
pair<ll,ll> dp[MAXN];
bool cmp(pair<ll,ll> x,pair<ll,ll> y){
if(x.second>=x.first){
if(y.second<y.first) return true;
return x.first<y.first;
}
else{
if(y.second>=y.first) return false;
return x.second>y.second;
}
}
vector<pair<ll,ll> > vec[MAXN];
int t,n,a[MAXN];
ll minn,now;
void get_ans(int pos,int fa){
vector<pair<ll,ll> > tor;
for(int i=0;i<vec[pos].size();++i){
pair<ll,ll> v=vec[pos][i];
if(v.first==fa) continue;
get_ans(v.first,pos);
dp[v.first]=make_pair(dp[v.first].first+v.second,dp[v.first].second-v.second);
if(dp[v.first].second<0)
dp[v.first]=make_pair(dp[v.first].first-dp[v.first].second,0);
tor.push_back(dp[v.first]);
}
sort(tor.begin(),tor.end(),cmp);
minn=now=a[pos];
for(int i=0;i<tor.size();++i){
pair<ll,ll> v=tor[i];
minn=min(minn,now-v.first);
now+=v.second-v.first;
}
if(minn>=0) dp[pos]=make_pair(0,now);
else dp[pos]=make_pair(-minn,now-minn);
}
ll u,v,dis;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
vec[i].clear();
}
for(int i=1;i<n;++i){
scanf("%lld%lld%lld",&u,&v,&dis);
vec[u].push_back(make_pair(v,dis));
vec[v].push_back(make_pair(u,dis));
}
get_ans(1,-1);
printf("%lld\n",dp[1].first);
}
}