POJ2054:Color a Tree 题解

思路

我最近一直在跟着李煜东写的算法进阶教程写题。这道题的限制条件有以下几个:

  • 必须在父节点染色的情况下才能进行对子节点的染色。
  • 要求最小的染色代价。

思考这两个条件,我们可以得出,只要我们优先选择平均损耗最大的子树进行染色,我们就可以得到最优解;另外,明显的是,如果我们给一个父节点染色,那么它的子节点中平均损耗最大的节点就会被优先染色。

为了让更多人理解,我打算分段代码来解释这个解法。

代码块

struct node
{
    int tim, cost, fat;
    double w;
} nums[maxn];

我们定义了一个数据结构,分别定义了次数、花费、父节点和平均权值。

int find_max()
{
    double val = 0;
    int id = 1;
    for (int i = 1; i <= n; i++)
        if (i != root && nums[i].w > val)
            id = i, val = nums[i].w;
    return id;
}

为了找到平均权值最大的节点,我们采用一种\(O(n)\)的方式来进行查找,我相信会有更优的解法,恕我只能在这写下其中一种。其中注意,我们在查找过程中需要把\(root\)节点排除在外。

int main()
{
    while (scanf("%d%d", &n, &root) && n != 0)
    {
        long long ans = 0;
        for (int i = 1; i <= n; i++)
            scanf("%d", &nums[i].cost), nums[i].w = nums[i].cost, nums[i].tim = 1, ans += nums[i].cost;
        for (int i = 1; i < n; i++)
            scanf("%d%d", &x, &y), nums[y].fat = x;
        for (int i = 1; i < n; i++)
        {
            int idx = find_max();
            nums[idx].w = 0;
            ans += nums[idx].cost * nums[nums[idx].fat].tim;
            for (int j = 1; j <= n; j++)
                if (nums[j].fat == idx)
                    nums[j].fat = nums[idx].fat;
            nums[nums[idx].fat].cost += nums[idx].cost;
            nums[nums[idx].fat].tim += nums[idx].tim;
            nums[nums[idx].fat].w = double(nums[nums[idx].fat].cost) / nums[nums[idx].fat].tim;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

接下来就是主函数了。我们先来看循环内的代码。我们首先是读入和初始化了\(nums[]\)数组,把损耗、次数初始化,并且向答案加上每个点的损耗(这是必须要加上的)。之后读入边。之后,我们开始进行计算。我们的范围在\([1,n)\)内,因为我们之前已经给答案加上了所有节点的基础值,而且总有一个结点的损耗值只需要乘上一,所以我们就可以把范围定在这个区间内。

我们寻找平均损耗最大的节点,并且将其损耗赋为\(0\),以便之后的查找会将其排除。之后我们把其子节点们全部合并到点\(idx\)的父亲节点上。之后,我们将两个节点的一些属性进行合并。

最后输出,得到答案。完整代码:

// POJ2054.cpp
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1010;
int n, root, x, y;
struct node
{
    int tim, cost, fat;
    double w;
} nums[maxn];
int find_max()
{
    double val = 0;
    int id = 1;
    for (int i = 1; i <= n; i++)
        if (i != root && nums[i].w > val)
            id = i, val = nums[i].w;
    return id;
}
int main()
{
    while (scanf("%d%d", &n, &root) && n != 0)
    {
        long long ans = 0;
        for (int i = 1; i <= n; i++)
            scanf("%d", &nums[i].cost), nums[i].w = nums[i].cost, nums[i].tim = 1, ans += nums[i].cost;
        for (int i = 1; i < n; i++)
            scanf("%d%d", &x, &y), nums[y].fat = x;
        for (int i = 1; i < n; i++)
        {
            int idx = find_max();
            nums[idx].w = 0;
            ans += nums[idx].cost * nums[nums[idx].fat].tim;
            for (int j = 1; j <= n; j++)
                if (nums[j].fat == idx)
                    nums[j].fat = nums[idx].fat;
            nums[nums[idx].fat].cost += nums[idx].cost;
            nums[nums[idx].fat].tim += nums[idx].tim;
            nums[nums[idx].fat].w = double(nums[nums[idx].fat].cost) / nums[nums[idx].fat].tim;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *