NOI2002 贪吃的九头龙

【问题描述】

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

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

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

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

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

例如图1所示的例子中,果树包含8个果子,7段树枝,各段树枝的“难受值”标记在了树枝的旁边。九头龙有两个脑袋,大头需要吃掉4个果子,其中必须包含最大的果子。即N=8,M=2,K=4:

大头吃4个果子,用实心点标识;

小头吃4个果子,用空心点标识;

九头龙的难受值为4,因为图中用细边标记的树枝被大头吃掉了。

图一描述了果树的形态,图二描述了最优策略。

【输入文件】

输入文件的第1行包含三个整数N (1<=N<=300),M (2<=M<=N),K (1<=K<=N)。 N个果子依次编号1,2,...,N,且最大的果子的编号总是1。第2行到第N行描述了果树的形态,每行包含三个整数a (1<=a<=N),b (1<=b<=N),c (0<=c<=105),表示存在一段难受值为c的树枝连接果子a和果子b。

【输出文件】

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

【样例输入】

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

【样例输出】

4时间限制:1 s   内存限制:128 MB题目大意:有一只M个头的九头龙,想吃掉有N个果实的树,要求每个小头至少吃1个果实,大头吃恰好K个果实,且树根上的果实也需由大头吃。对于一个果实,如果同一个头同时吃掉这个果实以及这个果实的父亲,就会受到一定的难受值,现在求吃掉这棵树的最小难受值。

首先判断无解,很明显仅当N<M+K-1时会无解。题目中头的数目2<=M<=N咋一看可能会被吓到,感觉无从下手,但仔细想一想M个脑袋是可以简化掉的,大致有两种情况:①M=2时 这时候大头和小头的难受值都需要考虑,但因为只有两个头还是可以解决的。②M>2时 这时候只需要考虑大头的难受值,因为小头完全可以避开吃掉树枝。然后为了方便转移,我们把可以把这棵多叉树转换成二叉树,这样问题就简单多了。到了这里这道题目就变成一道简单的树形DP了f[i][j][1/0] 表示以i为根的子树中,大头要吃j个果实,且其父结点也被大头吃了(1)或没被大头吃(0),的最小难受值转移方程详见程序,这里说个要注意的地方:对于一棵大头不吃任何果实的树,若M>2可以直接得出难受值为0,但是若M==2便还是要遍历到叶结点才结束。
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define R register
 5
 6 struct info{
 7   int to,pre,dis;
 8 }edge[1007];
 9
10 bool pd[307][307];
11 int n,m,k,size,last[307],head[307];
12 int f[307][307][2],fa[307],l[307],r[307],up[307];
13
14 void addline(int from,int to,int dis){
15   edge[++size].pre=head[from];head[from]=size;
16   edge[size].to=to;edge[size].dis=dis;
17   edge[++size].pre=head[to];head[to]=size;
18   edge[size].to=from;edge[size].dis=dis;
19 }
20
21 void tsdp(int k,int num){
22   if (pd[k][num]) return;
23   f[k][num][1]=f[k][num][0]=0x7ffffff;
24   if (!k||!num){ //处理0结点和大头不吃果实的情况
25     if (!num&&(m!=2||!k)) f[k][num][0]=f[k][num][1]=0;
26     if (m!=2||!k)return;
27   }
28   if (!l[k]&&!r[k]){ //处理叶结点
29     if (num==1) f[k][1][1]=up[k],f[k][1][0]=0;
30     if (!num) f[k][0][0]=up[k],f[k][0][1]=0;
31   }
32   for (R int i=0;i<=num;++i){ //状态转移
33     tsdp(l[k],i);
34     tsdp(r[k],num-i);
35     f[k][num][1]=std::min(f[k][num][1],f[l[k]][i][0]+f[r[k]][num-i][1]);
36     if (i)
37      f[k][num][1]=std::min(f[k][num][1],f[l[k]][i-1][1]+up[k]+f[r[k]][num-i][1]);
38     if (i)
39      f[k][num][0]=std::min(f[k][num][0],f[l[k]][i-1][1]+f[r[k]][num-i][0]);
40     if (m==2)
41      f[k][num][0]=std::min(f[k][num][0],f[l[k]][i][0]+up[k]+f[r[k]][num-i][0]);
42     else
43      f[k][num][0]=std::min(f[k][num][0],f[l[k]][i][0]+f[r[k]][num-i][0]);
44   }
45   pd[k][num]=true;
46 }
47
48 void DFS(int k){
49   for (R int i=head[k];i;i=edge[i].pre)
50   if (edge[i].to!=fa[k]){
51     fa[edge[i].to]=k;
52     DFS(edge[i].to);
53     up[edge[i].to]=edge[i].dis;
54   }
55 }
56
57 int main(){
58   scanf("%d%d%d",&n,&m,&k);
59   for (R int i=1;i<n;++i){
60     int a,b,c;
61     scanf("%d%d%d",&a,&b,&c);
62     addline(a,b,c);
63   }
64   DFS(1);
65   for (R int i=2;i<=n;++i){ //多叉转二叉
66     if (last[fa[i]]) r[last[fa[i]]]=i;
67     else l[fa[i]]=i;
68     last[fa[i]]=i;
69   }
70   tsdp(1,k);
71   if (n<k+m-1) printf("-1");
72   else printf("%d",f[l[1]][k-1][1]);
73 }
PS:这个程序写得太丑了,又臭又长,但又不想重写,凑合着看一下吧TAT

评测地址:https://www.vijos.org/p/1523

 
时间: 2024-10-21 17:03:12

NOI2002 贪吃的九头龙的相关文章

Vijos1523 NOI2002 贪吃的九头龙 树形dp

思路不算很难,但细节处理很麻烦 前面建图.多叉转二叉,以及确定dp处理序列的过程都是套路,dp的状态转移过程以注释的形式阐述 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 6 int N,M,K; 7 8 struct Edge 9 { 10 int to,next; 11 int weight; 12 void assign(int

Vijos 1523 贪吃的九头龙 【树形DP】

贪吃的九头龙 背景 安徽省芜湖市第二十七中学测试题 NOI 2002 贪吃的九头龙(dragon) Description:OfficialData:OfficialProgram:Converted by JackDavid127 描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落. 有一天,有M个脑袋的九头龙看到一棵长有N个果子的果树,喜出望外,

codevs贪吃的九头龙

传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落.有一天,有M 个脑袋的九头龙看到一棵长有N 个果子的果树,喜出望外,恨不得一口把它全部吃掉.可是必须照顾到每个头,因此它需要把N 个果子分成M组,每组至少有一个果子,让每个头吃一组.这M个脑袋中有一个最大,称为“大头”,是众头之首,它要吃掉恰好K个果子,而且K个果子中理所当然地应该包括唯一的一个最大的果子.果子由N

多叉树转二叉树+树形dp(codevs 1746 贪吃的九头龙 2002noi)

题目传送门 看到这个题目我们要先把问题简化了,条件中是多叉树,我们可以把它转换成二叉树,左边是儿子右边是兄弟的储存方式. 首先先判断否的部分,当总的果子小于需求,也就是N-k<M-1时输出-1. 我们再判断是的部分 如果没有大头,一定存在难受值为0的方案但是现在题目中有大头,我们就可以按按照小头的个数进行分类 1.有一个小头,我们要考虑小头和大头的难受值之和. 2.有多个小头,因为小头可以在奇偶的进行变换,所以我们只需要考虑大头的难受值. 分析到这里,我们就可以发现是树形dp我们设f[i][j]

vijos p1523 贪吃的九头龙(未完成)

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int point[303],next[603],v[603],c[603],cnt=0,N,M,K,f[303][303][2]; 6 bool p[303]; 7 void insect(int x,int y,int z){next[cnt]=point[x];point[x]=cnt;v[cn

codevs 1746 贪吃的九头龙

/* 状态定义的没错 就是考试的时候傻啦吧唧的转移左右孩子 其实之转移父亲就简单多了 不用考虑那么多 还有就是偷懒没有把谁有没有找过这个信息转过去 而是搞了个全局变量…wa到挺 再就是特盘的时候还有终止条件写的不好 写的时间也很长 ….. */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 510 #define inf 0x3f3f3f3f using namespace std;

贪吃的九头龙

树形dp #include<bits/stdc++.h> #define N 305 using namespace std; struct LEB{ int to,nxt,w; }e[N<<1]; int f[N][N][2]; int tmp[N][2]; int head[N]; int n,m,k,x,z,y,cnt; void Build(int x,int y,int z){ e[++cnt]=(LEB){y,head[x],z}; head[x]=cnt; } voi

美杜莎和九头蛇的对比

1.介绍 1.1九头蛇 每个密码安全性研究都显示,最大的安全漏洞是密码.该工具是概念证明代码,旨在为研究人员和安全顾问提供可能性,以显示从远程访问系统获得未经授权的访问将多么容易. 此工具仅用于法律目的! 已经有几种登录黑客工具,但是,没有一个工具支持多种协议来攻击或支持并行连接. 经过测试,它可以在Linux,Windows / Cygwin,Solaris,FreeBSD / OpenBSD,QNX(Blackberry 10)和MacOS上完全编译. 当前,该工具支持以下协议:Asteri

动归题目

[POJ1141]括号序列 [POJ1191]棋盘分割 [SPOJ196]决斗 [AOA]跳舞机 [AOA]积木游戏 [AOA]艺术馆的火灾 [AOA]机器人的名字 [UVa10559]方块消除 [AOA]公路巡逻 [POJ1074]并行期望值 [AOA]高性能计算机 [AOA]模板匹配 [AOA]不可解码的编码 [AOA]青蛙的烦恼 [AOA]排列问题 [AOA]最优排序二叉树 [POJ1038] Bugs公司 [UVa10531]迷宫统计 [AOA]贪吃的九头龙 [AOA]快乐的蜜月 [AO