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