topcoder SRM 643 解题报告

div2 1000pts

 题意:

有一棵树, N(N50) N ( N ≤ 50 ) 个节点,现在要给每个节点染两种颜色中的一种。对于一个节点,他的花费定义为以它为根的子树中颜色和它相同的节点个数。现在要求一种染色方案使得整棵树的花费之和最小(方案不用输出)。

 思路:

1.错误思路: f[u] f [ u ] 表示以 u u 为根的子树的最小cost,转移就是 f[u] f [ u ] += max(f[v],sz[v]f[v]) m a x ( f [ v ] , s z [ v ] − f [ v ] ) v v u的儿子。这样写出来已经有点贪心的意味了,只能保证子树里最大,不符合最优子结构性质。所以必须再开一维,使其有这种性质。
2. f f 数组开两维,f[u][i]表示以 u u 为根的子树中,有i个节点颜色和 u u 相同,价格的最小值。转移挺难想的,反正我想了好久好久,一直有点问题。转移像树上背包问题一样,每扫一个子树,就用这课子树的f[][]更新自身的 f[][] f [ ] [ ] f[u][i]=f[u][ik]+min(f[v][k],f[v][sz[v]k])+k|(isz[u],kmin(sz[v],j)) f [ u ] [ i ] = f [ u ] [ i − k ] + m i n ( f [ v ] [ k ] , f [ v ] [ s z [ v ] − k ] ) + k | ( i ≤ s z [ u ] , k ≤ m i n ( s z [ v ] , j ) ) ,sz[u]表示以u为根的子树中当前已经搜到的节点个数,for循环是倒着扫的,保证 f[u][ik] f [ u ] [ i − k ] 是没有更新当前 v v 子树时的。
  如何理解上述转移方程呢?可以把当前已经搜到过的u的子树和 u u 当做一棵树,新搜一个子树v,就把 v v 子树挂到u上。可以在 v v 子树上取k个节点与 u u 相同颜色,使总共有i个节点与 u u 相同颜色。然而不能把赋值操作改为与f[u][i] min m i n ,因为这棵子树是必须要挂上去的,也就是必须要算上他的价格,如果取 min m i n 他的价格就不会被算进去了。(看起来很明显的事实开题之后我想了好久)
  最后的问题就是初始化了,在扫 u u 的子树之前,先把f[u][1]赋值成 1 1 ,相当于u和它的 0 0 棵子树组成的只有1个节点的子树中,有 1 1 个与自己颜色相同的价格最小为1

p.s.思路实在是不很清晰,算是对着样例打出来的程序,讲的肯定也不是很清楚,具体还请看代码

贴代码:

#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9+10;
const int N = 100;
int n;
vector<int> to[N];
int f[N][N], sz[N];

void DFS(int u, int fa)
{
    sz[u] = 1;
    f[u][1] = 1;
    for (int i = 0; i < to[u].size(); i++){
        int v = to[u][i];
        if (v == fa) continue;
        DFS(v, u);
        sz[u] += sz[v];
        for (int j = sz[u]; j >= 1; j--){
            int tmp = INF;
            for (int k = 0; k <= min(sz[v], j); k++)
                tmp = min(tmp, f[u][j-k]+min(f[v][k], f[v][sz[v]-k])+k);
            f[u][j] = tmp;
        }
    }
}

class TheKingsTree {
public:
    int getNumber( vector <int> parent );
};
int TheKingsTree::getNumber(vector <int> parent) {
    //initialize
    n = parent.size()+1;
    for (int i = 0; i < n; i++)
        to[i].clear();
    for (int i = 0; i < n-1; i++){
        to[parent[i]].push_back(i+1);
        to[i+1].push_back(parent[i]);
    }
    memset(f, 0x3f, sizeof(f));
    //main
    DFS(0, -1);
    int ans = INF;
    for (int i = 1; i <= n; i++)
        ans = min(ans, f[0][i]);
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值