Acwing 115 给树染色 (贪心)

题面

一颗树有 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
输出样例:
33

思路

太难....是这样的,我们首先考虑没有树形结构的情况,我们应该如何染色,对的,我们当然想到排序不等式,先染权值大的。那么加了树的结构限制说明了什么,说明了我们在给一个点染色之前,我们必须先给它的父亲染色,所以当我们沿着原来的思路去想的时候,就会发现,如果这个时候我们想要染一个权值最大的点,就必须先去满足他的父亲。那么我们其实可以把a和b看成一个节点一起染色,那么问题最后一定会变成,这一对点和另外一个点的大小关系,当我们考虑两种情况并且做差比较的时候可以发现,如果我们要先染点对,那么符合的条件是c小于点对的均值,拓展到点对和点对直接也是一样。基于这样的思路,我们每次找出点的平均值最大的点,把他并入父亲,更新ans并且同时打上lazy tag,最后在n-1次的更新后,得到最后的ans。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1010;
int n,root;
struct node {
    int father,sum;
    int size;
    double avg;
}a[maxn];
inline int find () {
    double avg=0;
    int ans=-1;
    for (int i=1;i<=n;i++) {
        if (a[i].avg>avg&&i!=root) {
            ans=i;
            avg=a[i].avg;
        }
    }
    return ans;
}
int main () {
   int ans=0;
   cin>>n>>root;
   for (int i=1;i<=n;i++) {
       node &nd=a[i];
       nd.size=1;
       cin>>nd.sum;
       nd.avg=nd.sum;
       ans+=nd.sum;
   }
   for (int i=1;i<=n;i++) {
       int x,y;
       cin>>x>>y;
       a[y].father=x;
   }
   for (int i=0;i<n-1;i++) {
       int ver=find ();
       int f=a[ver].father;
       ans+=a[ver].sum*a[f].size;
       a[ver].avg=-1;
       for (int j=1;j<=n;j++) {
           if (a[j].father==ver) a[j].father=f;
       }
       a[f].size+=a[ver].size;
       a[f].sum+=a[ver].sum;
       a[f].avg= (double ) a[f].sum/a[f].size;
   }
   cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/hhlya/p/13332626.html