码迷,mamicode.com
首页 > Windows程序 > 详细

AcWing 给树染色

时间:2019-08-04 01:31:24      阅读:119      评论:0      收藏:0      [点我收藏+]

标签:贪心   desc   ++   整理   mes   实时   空格   题解   cin   

AcWing 给树染色

Description

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

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

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

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

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

Input

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

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

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

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

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

Output

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

Data Size

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

Sample Input

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

Sample Output

33

题解:

  • 贪心。
  • 妙题,妙不可言。
  • 首先如果染色没有限制怎么办?那么肯定先染权值最大的,然后染权值次大的… …跟[打水问题]类似。
  • 那么染色有了限制怎么办?那么对于权值最大的结点,如果它父亲被染了,它肯定立即染上颜色。
  • 所以,因为权值最大的点和其父亲的染色是连续的,我们就把它们俩看作一个整体,即可以合并成一个新点
  • 那么现在假设有3个点x、y、z,x是y的父亲,y是整棵树里权值最大的点。所以把x、y看作一个整体(x, y)。
  • 接下来就有两种染色方案
    1. (x,y)先染,z后染
    2. z先染,(x,y)后染
  • 两种情况分别的代价是x + 2y + 3z和z + 2x + 3y
  • “微扰法”,假设(x,y)先染比z先染优,那么就有x + 2y + 3z > z + 2x + 3y
  • 化简得到:z < (x + y) / 2。说明如果一个整体的平均值比另一个点大,那么先染这个整体更优。
  • 所以到这里我们可以整理出算法流程:每次找出当前权值最大的非根节点,将其染色顺序排在紧随父节点之后的位置,然后将该点合并进父节点中,更新父节点的权值。直到将所有点都合并进根节点为止。
  • 得出了染色顺序后,模拟染色即可得出答案。
  • 但是这个染色顺序求起来挺麻烦的(其实也不难吧,用平衡树维护下就好了,我没试过)(也可以过)
  • 所以我们还可以在合并的时候实时更新ans,O(1)更新,巧妙的设计!(常用的技巧
  • 最初所有点各自为一组,总分值是 S=∑ai?1 (1 <= i <= n)
    接下来每次会将两组点合并,将其中一组点接在另一组点的后面。比如两组点分别是 xi和 yi,我们将 yi 接在 xi 之后,则 yi 中每个点所乘的系数均会增加一个相同的偏移量,这个偏移量就是 xi 中点的个数,假设是 k,则合并之后,总的权值直接加上 k?∑yi 即可。
#include <iostream>
#include <cstdio>
#define N 1005
using namespace std;

struct A {int sum, size, fat; double avg;} a[N];
int n, root, ans;

int find()
{
    int pos; double res = -1;
    for(int i = 1; i <= n; i++)
        if(i != root && a[i].avg > res)
            res = a[i].avg, pos = i;
    return pos;
}

int main()
{
    cin >> n >> root;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i].sum;
        a[i].size = 1;
        a[i].avg = a[i].sum;
        ans += a[i].sum;
    }
    for(int i = 1; i < n; i++) 
    {
        int u, v;
        cin >> u >> v;
        a[v].fat = u;
    }
    for(int i = 1; i < n; i++)
    {
        int p = find(), father = a[p].fat;
        ans += a[father].size * a[p].sum;
        a[father].sum += a[p].sum;
        a[father].size += a[p].size;
        a[father].avg = (double)a[father].sum / (double)a[father].size;
        a[p].avg = -1;
        for(int j = 1; j <= n; j++)
            if(a[j].fat == p) a[j].fat = father;
    }
    cout << ans;
    return 0;
}

AcWing 给树染色

标签:贪心   desc   ++   整理   mes   实时   空格   题解   cin   

原文地址:https://www.cnblogs.com/BigYellowDog/p/11296812.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!