codevs1746 贪吃的九头龙

【问题描述】
传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落。
有一天,有 M 个脑袋的九头龙看到一棵长有 N 个果子的果树,喜出望外,恨不得一口把它全部吃掉。可是必须照顾到每个头,因此它需要把 N 个果子分成 M 组,每组至少有一个果子,让每个头吃一组。
这 M 个脑袋中有一个最大,称为“大头”,是众头之首, 它要吃掉恰好 K 个果子,而且 K 个果子中理所当然地应该包括唯一的一个最大的果子。 果子由 N-1根树枝连接起来,由于果树是一个整体,因此可以从任意一个果子出发沿着树枝“走到”任何一个其他的果子。
对于每段树枝,如果它所连接的两个果子需要由不同的头来吃掉,那么两个头会共同把树枝弄断而把果子分开;如果这两个果子是由同一个头来吃掉,那么这个头会懒得把它弄断而直接把果子连同树枝一起吃掉。当然,吃树枝并不是很舒服的,因此每段树枝都有一个吃下去的“难受值”,而九头龙的难受值就是所有头吃掉的树枝的“难受值”之和。
九头龙希望它的“难受值”尽量小,你能帮它算算吗?
例如图 1 所示的例子中,果树包含 8 个果子,7 段树枝,各段树枝的“难受值”标记在了树枝的旁边。九头龙有两个脑袋,大头需要吃掉 4 个果子,其中必须包含最大的果子。即 N=8,M=2,K=4:
最大的果子大头吃 4 个果子,用实心点标识;小头吃 4 个果子,用空心点标识;九头龙的难受值为 4,因为图中用细边标记的树枝被大头吃掉了。
【输入文件】
输入文件 dragon.in 的第 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<=10 5 ),表示存在一段难受值为 c 的树枝连接果子 a 和果子 b。
【输出文件】
输出文件 dragon.out 仅有一行,包含一个整数,表示在满足“大头”的要求的前提下,九头龙的难受值的最小值。如果无法满足要求,输出-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
【样例说明】
该样例对应于题目描述中的例子。

 

 

正解:树形DP

解题报告:

  今天考试T4,居然是NOI原题。

  显然是一道树形DP,但是我们考虑记录的状态是什么:f[0、1][i][j]表示以i为根结点的子树中1号吃了j个并且i不选或者选的最小值。同时记忆化。

  直接分类讨论儿子结点选了多少个,总复杂度O(N^3)。

  代码如下:

 

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long LL;
15 const int MAXN = 311;
16 LL inf;
17 int n,m,k,ecnt;
18 int first[MAXN],next[MAXN*2],to[MAXN*2];
19 int brother[MAXN],son[MAXN],lian[MAXN][MAXN];
20 bool vis[MAXN];
21 LL f[2][MAXN][MAXN];
22 
23 inline int getint()
24 {
25        int w=0,q=0; char c=getchar();
26        while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
27        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
28 }
29 
30 inline void down(int x,int fa){
31     vis[x]=1;
32     for(int i=first[x];i;i=next[i]) {
33     int v=to[i]; if(vis[v]) continue;
34     //printf("%d %d\n",v,brother[v]); 
35     brother[v]=son[x]; son[x]=v; down(v,x);
36     }
37 }
38 
39 inline LL dfs(int x,int fa,int remain,int flag){
40     if(x==0) { if(remain==0) return 0; else return -1; }
41     if(f[flag][x][remain]!=-1) return f[flag][x][remain];
42     LL minl=inf,nowl,nowr,suan;
43     for(int i=0;i<=remain;i++) {
44     nowl=dfs(brother[x],fa,i,flag);
45     if(nowl==remain) nowr=-1; 
46     else nowr=dfs(son[x],x,remain-i-1,1);//
47     
48     /*if(nowl>=0) {    
49       if(flag==1) { suan=nowl+nowr+lian[fa][x]; } else suan=nowl+nowr;   
50       if(suan<minl) minl=suan;    }
51     */
52 
53     if(nowl>=0 && nowr>=0) {
54         if(flag==1) { suan=nowl+nowr+lian[fa][x]; } else suan=nowl+nowr;
55         minl=min(minl,suan);
56     } 
57     nowr=dfs(son[x],x,remain-i,0);//不选
58     /*if(nowl>=0) {
59         if(flag==0 && m==2) suan=nowl+nowr+lian[fa][x]; else suan=nowl+nowr;
60         minl=min(minl,suan);
61         }*/
62     if(nowl>=0 && nowr>=0) {
63         if(flag==0 && m==2) suan=nowl+nowr+lian[fa][x]; else suan=nowl+nowr;
64         minl=min(minl,suan);
65     }
66     }
67     //printf("%d %d %d %d\n",x,fa,remain,flag);
68     if(minl==inf) f[flag][x][remain]=-inf; else f[flag][x][remain]=minl; 
69     return f[flag][x][remain];
70 }
71 
72 inline void work(){
73     n=getint(); m=getint(); k=getint(); inf=1; for(int i=1;i<=60;i++) inf*=2;
74     int x,y,z;
75     for(int i=1;i<n;i++) {
76     x=getint(); y=getint(); z=getint(); lian[x][y]=lian[y][x]=z;
77     next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y;
78     next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x;
79     }
80     if(m-1+k>n) { printf("-1"); return ; }
81     down(1,0); memset(f,-1,sizeof(f));
82     dfs(son[1],1,k-1,1);
83     printf("%lld",f[1][son[1]][k-1]);
84 }
85 
86 int main()
87 {
88   work();
89   return 0;
90 }

 

posted @ 2016-09-18 15:35  ljh_2000  阅读(221)  评论(0编辑  收藏  举报