POJ 2054 Color a Tree(树上贪心)

题目链接
题意

给你一个树,告诉你根节点。树上每个节点有一个权值 w i w_i wi,你需要将树的每个节点进行染色。
每次染色花费 1 1 1 的时间,一个点可以染色需要满足,根节点或者相邻节点已经被染色。
染色的花费为 时 间 ∗ w i 时间*w_i wi,求全部染色的最小花费。

思路

首先权值最大点和其父节点染色操作是连续的。
假设 x , y , z x,y,z x,y,z 三点, x x x y y y 染色连续
先染 x , y x,y x,y,再染 z z z 花费为 x + 2 ∗ y + 3 ∗ z x+2*y+3*z x+2y+3z
先染 z z z,再染 x , y x,y x,y 花费为 z + 2 ∗ x + 3 ∗ y z+2*x+3*y z+2x+3y

x + 2 ∗ y + 3 ∗ z ? 2 ∗ x + 3 y + z x+2*y+3*z ? 2*x+3y+z x+2y+3z2x+3y+z
= ( x + y ) / 2 + 2 ∗ z ? z + 2 ∗ ( ( x + y ) / 2 ) =(x+y)/2+2*z ? z+2*((x+y)/2) =(x+y)/2+2z?z+2((x+y)/2)
= z ? ( x + y ) / 2 =z?(x+y)/2 =z?(x+y)/2

判断如何染色,可将最大值归入其父节点,得到一个新的等效节点(等效节点 = 该点包含权值总和÷该点包含节点数)。
继续缩点,直到全部的点都被判断完。
缩点转移可使用并查集,向根方向合并

代码
#include <stdio.h>
#include <string.h>
using namespace std;

int w[1005], f[1005], num[1005], ans[1005], pre[1005];

int getf(int a)
{
    return a == pre[a] ? a : getf(pre[a]);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
#endif
	int n, r;
	while(scanf("%d%d",&n,&r), n)
	{
        f[r] = 0;
        for(int i = 1; i <= n; ++i) scanf("%d",&w[i]), num[i] = 1, pre[i] = i;
	    for(int i = 1; i < n; ++i)
        {
            int v, u;
            scanf("%d%d",&v,&u);
            f[u] = v;
        }
        pre[0] = 0;
        num[0] = 1;
        memset(ans,0,sizeof(ans));
        for(int i = 1; i <= n; ++i)
        {
            int a = 0;
            for(int j = 1; j <= n; ++j)
            {
                if(pre[j] == j && (!a || w[a]*num[j] < num[a]*w[j])) a = j;
            }
            int fa = getf(f[a]);
            ans[fa] += ans[a]+w[a]*num[fa];
            w[fa] += w[a];
            num[fa] += num[a];
            pre[a] = fa;
        }
        printf("%d\n",ans[0]);
	}
	return 0;
}
/*
6 1
2 1 6 5 5 4
1 2
2 3
3 4
3 5
1 6
0 0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值