AcWing 给树染色

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;
}

转载于:https://www.cnblogs.com/BigYellowDog/p/11296812.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
节点染色是指给数据结构中的每个节点分配一种颜色或标记,以此来帮助解决问题或进行一些特定操作。在Python中,可以通过以下方法来实现节点染色。 首先,我们需要定义一个节点的类,用来表示每个节点的属性和方法。该类可以包括节点的值、子节点列表、以及染色的属性等。例如: ```python class TreeNode: def __init__(self, value): self.value = value self.children = [] self.color = None ``` 接下来,我们可以定义一个函数,来对节点进行染色操作。该函数可以使用递归的方式,不断遍历的每个节点,并为其分配颜色。例如,可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来遍历节点,并依次给每个节点染色。具体的染色规则可以根据问题的需求来定义。 以下是一个简单的示例,使用DFS对节点进行染色: ```python def color_tree_dfs(node, colors): if node is None: return for child in node.children: color_tree_dfs(child, colors) # 给当前节点染色 if colors: node.color = colors.pop(0) else: node.color = "No color available" ``` 以上代码中,节点的颜色是通过一个颜色列表进行分配的。函数每次从颜色列表中取出一个颜色,对当前节点进行染色,并将列表中的该颜色移除。如果颜色列表为空,则当前节点将被标记为"No color available"。 通过这样的方式,我们可以对节点进行染色操作,并实现根据问题需求设置节点颜色。当然,具体染色的规则和递归的方式可能因实际问题的不同而有所调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值