Skip to content

11079: 【原1079】贪吃的九头龙

题目

题目描述

author: 寿鹤鸣 原OJ链接:https://acm.sjtu.edu.cn/OnlineJudge-old/problem/1079

Description

传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落。

有一天,有M个脑袋的九头龙看到一棵长有N个果子的果树,喜出望外,恨不得一口把它全部吃掉。可是必须照顾到每个头,因此它需要把N个果子分成M组,每组至少有一个果子,让每个头吃一组。

这M个脑袋中有一个最大,称为“大头”,是众头之首,它要吃掉恰好K个果子,而且K个果子中理所当然地应该包括唯一的一个最大的果子。果子由N-1根树枝连接起来,由于果树是一个整体,因此可以从任意一个果子出发沿着树枝“走到”任何一个其他的果子。

对于每段树枝,如果它所连接的两个果子需要由不同的头来吃掉,那么两个头会共同把树枝弄断而把果子分开;如果这两个果子是由同一个头来吃掉,那么这个头会懒得把它弄断而直接把果子连同树枝一起吃掉。当然,吃树枝并不是很舒服的,因此每段树枝都有一个吃下去的“难受值”,而九头龙的难受值就是所有头吃掉的树枝的“难受值”之和。

九头龙希望它的“难受值”尽量小,你能帮它算算吗?

Input Format

第1行包含三个整数\( N ( 1 \leq N \leq 300 ),M (2 \leq M \leq N),K (1 \leq K \leq N) \)。N个果子依次编号1,2,...,N,且最大的果子的编号总是1。

第2行到第N行描述了果树的形态,每行包含三个整数\( a (1 \leq a \leq N),b (1 \leq b \leq N),c (0 \leq c \leq 105) \),表示存在一段难受值为c的树枝连接果子a和果子b。

Output Format

仅有一行,包含一个整数,表示在满足“大头”的要求的前提下,九头龙的难受值的最小值。如果无法满足要求,输出-1。

说明

提示

对于无解的情况,只要判断果子是否够吃就行,即:M+K-1>N则无解

可以注意到当\(n \geq 3 \)时,最优解一定不会存在两个果子同时被一个小头所吃,因为可以让其中一个让另一个头吃,一方面减少了难受值,另一方面可以更满足每个头至少吃一个的条件。所以我们只用考虑两端都被大头吃的情况。 我们令状态d[i,j,0]表示i的父亲节点为小头吃,i的右边所有兄弟及其子树以及i这颗子树大头吃的一共有j个,最小的难受值。d[i,j,1]为i的父亲为大头吃。 则令child[i]为i的最左儿子,sibling[i]为i的右兄弟。

Source: NOI 2002

Sample Input

8 2 4
1 2 20
1 3 4 
1 4 13
2 5 10
2 6 12
3 7 15
3 8 5

Sample Output

4

FineArtz's solution

/* 贪吃的九头龙 */
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

const int INF = 1000000000;

class Node{
public:
    int child = 0, sibling = 0, father = 0;
    int lenf = 0;
    int ecnt = 0, edge[305] = {0}, w[305] = {0};
};

int n, m, k;
Node a[305];
int f[305][305][2] = {0};

void makeTree(int x, int f){
    a[x].father = f;
    if (a[x].ecnt == 1)
        return;
    for (int i = 1; i <= a[x].ecnt; ++i){
        if (a[x].edge[i] != f){
            if (a[x].child == 0){
                a[x].child = a[x].edge[i];
                a[a[x].child].lenf = a[x].w[i];
            }
            else{
                int t = a[x].child;
                while (a[t].sibling != 0)
                    t = a[t].sibling;
                a[t].sibling = a[x].edge[i];
                a[a[t].sibling].lenf = a[x].w[i];
            }
            makeTree(a[x].edge[i], x);
        }
    }
}

void printTree(int x){
    if (x == 0)
        return;
    cout << x << endl;
    printTree(a[x].child);
    printTree(a[x].sibling);
}

int factor(int x, int y){
    if (x > 0 && y > 0)
        return 1;
    if (x == 0 && y == 0 && m == 2)
        return 1;
    return 0;
}

int dp(int x, int k, int b){
    if (f[x][k][b] != -1)
        return f[x][k][b];
    int ret = INF;
    for (int i = 0; i <= k; ++i){
        int t = dp(a[x].child, i, 0) + dp(a[x].sibling, k - i, b) + factor(b, 0) * a[x].lenf;
        ret = min(ret, t);
    }
    for (int i = 0; i < k; ++i){
        int t = dp(a[x].child, i, 1) + dp(a[x].sibling, k - i - 1, b) + factor(b, 1) * a[x].lenf;
        ret = min(ret, t);
    }
    f[x][k][b] = ret;
    return ret;
}

int main(){
    cin >> n >> m >> k;
    if (m + k - 1 > n){
        cout << "-1" << endl;
        return 0;
    }
    for (int i = 1; i <= n - 1; ++i){
        int u, v, w;
        cin >> u >> v >> w;
        a[u].edge[++a[u].ecnt] = v;
        a[u].w[a[u].ecnt] = w;
        a[v].edge[++a[v].ecnt] = u;
        a[v].w[a[v].ecnt] = w;
    }
    makeTree(1, 0);
    memset(f, -1, sizeof(f));
    f[0][0][0] = f[0][0][1] = 0;
    for (int i = 1; i <= k; ++i){
        f[0][i][0] = INF;
        f[0][i][1] = INF;
    }
    cout << dp(a[1].child, k - 1, 1) << endl;
    return 0;
}