hiho一下 第109周 Tower Defense Game 树DP+贪心

题目大意

给定一颗以1为根节点的树,每个节点有一个购入价格p和卖出价格q

进入一个节点时需要花费p,离开时可以收回q,每个节点只产生一次购入和卖出。

请你选择一个遍历的顺序,要求在遍历的过程中身上的钱数不小于0,且出发时带的钱数最少。

按照遍历的顺序是指:当你选择了一颗子树之后,你需要将这个子树全部走完,才能选择其他子树

 

.....................................

简化到二叉树的情况考虑,

根节点A的左右儿子分别为B和C,

如果最后停留在B,则开始带的最多钱为 min(  MAX(p1,p1-q1+p2-q2+p3),max(p1,p1-q1+p3-q3+p2) )

显然根据p>=q

答案就是min(p1-q1+p2-q2+p3,p1-q1+p3-q3+p2);

因此可以看到,比较的是 psum-(qsum-qx)  x为最后停留的一边

因此对于二叉树的情况,我们应该从qi先选大后选小

同理可以推广到多叉树的情况,还是按qi从大到小选.

 

刚才从多个儿子里面按顺序选的 条件是:qi先选大后选小

此时若儿子代表是一棵树的话,pi和qi的意思就应该变成了,选这棵树 所需要的最大初始钱,和返回时会剩余的钱。

这个并不是简单的把子树所有节点的p和q相加,

 

 

 

需要递归的过程中先算出每个子树的pt_i和pt_i才可以。 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=13240;
vector<int >mp[N];
ll p[N],q[N];
bool cmp(int a,int b)
{
    return q[a]>q[b];   //按照qi排序
}
ll get(int x,int fa)
{
    for(int i=0; i<mp[x].size(); i++)
    {
        int v=mp[x][i];
        if (v==fa)continue;
        get(v,x);   //先递归算出所有子树的p和q
    }
    ll &t=p[x]; //取出进入x为根子树至少需要多少初始钱
    ll c=p[x]-q[x]; //经过x后剩余的钱
    sort(mp[x].begin(),mp[x].end(),cmp);//按q排序
    for(int i=0; i<mp[x].size(); i++)
    {
        int v=mp[x][i];
        if (v==fa)continue;
        t=max(t,p[v]+c);//用剩余的c+p[v]去更新初始钱
        c+=p[v]-q[v];   //剩余的钱递增
    }
    q[x]=t-c;//更新qx
}
int main()
{
    int n;
    int x,y;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&p[i],&q[i]);
    for(int i=1; i<n; i++)
    {
        scanf("%d %d",&x,&y);
        mp[x].push_back(y);
        mp[y].push_back(x);
    }
    get(1,0);
    printf("%lld\n",p[1]);
    return 0;
}


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值