给树染色(贪心+树上操作)

题目描述:

一颗树有 n 个节点,这些节点被标号为:1,2,3…n,每个节点 i 都有一个权值 A[i]。

现在要把这棵树的节点全部染色,染色的规则是:

根节点R可以随时被染色;对于其他节点,在被染色之前它的父亲节点必须已经染上了色。

每次染色的代价为T*A[i],其中T代表当前是第几次染色。

求把这棵树染色的最小总代价。

输入格式

第一行包含两个整数 n 和 R ,分别代表树的节点数以及根节点的序号。

第二行包含 n 个整数,代表所有节点的权值,第 i 个数即为第 i 个节点的权值 A[i]。

接下来n-1行,每行包含两个整数 a 和 b ,代表两个节点的序号,两节点满足关系: a 节点是 b 节点的父节点。

除根节点外的其他 n-1 个节点的父节点和它们本身会在这 n-1 行中表示出来。

同一行内的数用空格隔开。

输出格式

输出一个整数,代表把这棵树染色的最小总代价。

数据范围

1≤n≤1000,
1≤A[i]≤1000

输入样例:

5 1
1 2 1 2 4
1 2
1 3
2 4
3 5
0 0

输出样例:

33

这道题北大yxc学长讲的非常好,下面是他的讲解

AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<stack>
#include<vector>
#include<map>
const int INF = 0x3f3f3f3f;
using namespace std;
typedef long long ll;
typedef double ld;
const int N=1010;
int n, root;
struct Node
{
    int p,s,v;
    double avg;
} nodes[N];

int find()
{
    double avg=0;
    int res=-1;
    for (int i=1; i<=n; i++ )
        if (i!=root&&nodes[i].avg>avg)
        {
            avg=nodes[i].avg;
            res=i;
        }
    return res;
}//找到除去根节点最大权值的点

int main()
{
    cin>>n>>root;
    int ans=0;
    for (int i=1; i<=n; i++)
    {
        cin>>nodes[i].v;
        nodes[i].avg=nodes[i].v;
        nodes[i].s=1;
        ans+=nodes[i].v;
    }
    for (int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        nodes[b].p=a;
    }
    for (int i=0;i<n-1;i++)
    {
        int p=find();
        int father=nodes[p].p;//更新最大权值的父节点
        ans+=nodes[p].v*nodes[father].s;//当前点的权值*父节点被染色的次数
        nodes[p].avg=-1;//这个点已经染色更新权值
        for (int j=1;j<=n;j++)
            if(nodes[j].p==p)
                nodes[j].p=father;//为根节点并到当前节点
        nodes[father].v+=nodes[p].v;
        nodes[father].s+=nodes[p].s;
        nodes[father].avg=(double)nodes[father].v/nodes[father].s;
    }
    cout<<ans<<endl;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值