HDU 1055

题目大意:给你一棵树,数有N个节点,每个节点有权重w。你需要给这棵树的每一个节点进行涂色,每涂一个节点花费一个单位时间,开始时间设定为0,对于一个权重为W的节点,假定你在第T个的单位时间完成对它的涂色,那么对它的涂色的代价C=W*T,同时规定只有当某个节点父节点已经完成涂色时,你才能对它进行涂色操纵。要求你求出完成整颗树的涂色所需要的最小的花费。


题解:这是道贪心的题目。首先我们看代价计算公式:C=T*W,我们很容易想到权重越大的节点我们应该尽早涂色,但限于规定--只有当某个节点父节点已经完成涂色时,你才能对它进行涂色,所以我们这样简易的想法显然是行不通的。虽然当前权重最大的节点我们不一定可以马上进行涂色,但不难想到,该节点的涂色一定是紧接着它的父亲节点的。于是,假设当前权重最大的节点为k,它的父亲节点为p,我们可以将这两个节点合并成一个新节点p',p'的父亲节点为p的父亲节点,p'的子节点为p以及k的子节点,我们对p'节点涂色所需时间p’.t=p.t+k.t,节点p'的总权重p'.c=p.c+k.c,节点p的平均权重p'.w=p'.c/p'.t。对于新节点p'涂色花费时间的更新我觉得很好理解,至于对p'.c的更新我是这么理解的,节点p'每晚一个单位时间进行涂色,对p'进行涂色所需要花费的代价就要增加p'.c。对于p'的平均权重p'.w,它具有很重要的比较意义,我们是通过它来判断当前该被合并的节点是哪个。那为什么会有p'.w=p'.c/p'.t呢?假设现在有两个节点A,B,当我们把节点A放在节点B前面进行涂色时,节点B进行涂色增加的代价为ExCost_B=B.c*A.t,当我们把节点B放在节点A前面进行涂色时,节点A进行涂色增加的代价为ExCost_A=A.c*B.t,同时无论节点A或B谁在前面,整个过程的基础代价(一定会花费的代价)都一样。于是通过比较ExCost_A和ExCost_B就可以得知那个方案更优,当有ExCost_A>ExCost_B ==> A.c*B.t>B.c*A.t ==> A.c/A.t>B.c/B.t ==> A.w>B.w,也就是当A的平均权重大于B的时,应该把节点A放在节点B的前面(即对节点A先进行涂色)。


代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,r,ans;
struct node
{
    int pre,c,t;
    double w;
}e[1505];

int main(){
    while(scanf("%d%d",&n,&r),n+r){
        int u,v;
        ans=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&e[i].c);
            e[i].t=1;
            e[i].w=e[i].c;
            ans+=e[i].c;
        }
        for(int i=1;i<n;++i){
            scanf("%d%d",&u,&v);
            e[v].pre=u;
        }
        for(int i=1;i<n;++i){
            double maxn=-1;
            int k;
            for(int i=1;i<=n;++i){
                if(i==r) continue;
                if(e[i].w>maxn){
                    maxn=e[i].w;
                    k=i;
                }
            }
            int p=e[k].pre;
            ans+=e[k].c*e[p].t;
            e[k].w=0;
            for(int i=1;i<=n;++i)
                if(e[i].pre==k)
                e[i].pre=p;
            e[p].c+=e[k].c;
            e[p].t+=e[k].t;
            e[p].w=e[p].c*1.0/e[p].t;
        }
        printf("%d\n",ans);
    }
    return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值