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 }