POJ 2054 Color a Tree 贪心

时空隧道


题目大意:
给出一棵有根树(有向边),每个节点权值为val[i],要求把所有点组成一个序列并且父亲节点排在子节点前面,使得ans=val[1]*1+val[2]*2+……+val[n]*n最小…


分析:
看到此题第一想法就是val大的放前面…但是这是在没有限制的情况下…如果我们现在限制父节点一定比子节点靠前那么对于val大的子节点来说当然是紧挨着父节点最好…所以我们可以得到一些二元组(也就是把子节点和父节点绑定)…
现在我们有xy这个二元组和z这个单独节点,考虑怎样排列使得结果最优…
假设先选xy优于先选z,那么我们可以得到式子i*x+(i+1)*y+(i+2)*z < i*z+(i+1)*x+(i+2)*y…化简得到x+y>2*x,也就是(x+y)/2>x…这不就是说x+y的平均值大的优先选么…
现在我们把二元组和点扩展为两个序列…可以得到同样的结论…平均值大的优先选…所以我们只要每次选出val最大的点,然后把当前节点与其父节点合并(val变为平均值)就可以确定序列的顺序了…


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
const int maxn=1000+5;
int n,r,fa[maxn],sum[maxn],tim[maxn],cnt,ans;
double val[maxn];
signed main(void){
    while(scanf("%d%d",&n,&r)&&!(!n&&!r)){
        cnt=ans=0;
        for(int i=1;i<=n;i++)
            scanf("%lf",&val[i]),ans+=(int)val[i],sum[i]=val[i],tim[i]=1;
        for(int i=1,x,y;i<n;i++)
            scanf("%d%d",&x,&y),fa[y]=x;//有向图 
        while(cnt<n-1){
            cnt++;int v;double MAX=0;//每次找序列中最大的进行合并 
            for(int i=1;i<=n;i++)
                if(val[i]>MAX&&i!=r)
                    MAX=val[i],v=i;
            for(int i=1;i<=n;i++)
                if(fa[i]==v)
                    fa[i]=fa[v];//合并之后要更新当前点的子节点的fa 
            ans+=sum[v]*tim[fa[v]];val[v]=0;//tim[fa[v]]就是当前子序列中当前点前面有多少个点 
            tim[fa[v]]+=tim[v];sum[fa[v]]+=sum[v],val[fa[v]]=(double)sum[fa[v]]/(double)tim[fa[v]];
        }
        cout<<ans<<endl;
    }
    return 0;
}

by >_< NeighThorn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值