標籤:
傳說中的九頭龍是一種特別貪吃的動物。雖然名字叫“九頭龍”,但這隻是
說它出生的時候有九個頭,而在成長的過程中,它有時會長出很多的新頭,頭的
總數會遠大於九,當然也會有舊頭因衰老而自己脫落。
有一天,有M 個腦袋的九頭龍看到一棵長有N 個果子的果樹,喜出望外,
恨不得一口把它全部吃掉。可是必須照顧到每個頭,因此它需要把N 個果子分
成M組,每組至少有一個果子,讓每個頭吃一組。
這M個腦袋中有一個最大,稱為“大頭”,是眾頭之首,它要吃掉恰好K個
果子,而且K個果子中理所當然地應該包括唯一的一個最大的果子。果子由N-1
根樹枝串連起來,由於果樹是一個整體,因此可以從任意一個果子出發沿著樹枝
“走到”任何一個其他的果子。
對於每段樹枝,如果它所串連的兩個果子需要由不同的頭來吃掉,那麼兩個
頭會共同把樹枝弄斷而把果子分開;如果這兩個果子是由同一個頭來吃掉,那麼
這個頭會懶得把它弄斷而直接把果子連同樹枝一起吃掉。當然,吃樹枝並不是很
舒服的,因此每段樹枝都有一個吃下去的“難受值”,而九頭龍的難受值就是所
有頭吃掉的樹枝的“難受值”之和。
九頭龍希望它的“難受值”盡量小,你能幫它算算嗎?
例 1 所示的例子中,果樹包含8 個果子,7 段樹枝,各段樹枝的“難受
值”標記在了樹枝的旁邊。九頭龍有兩個腦袋,大頭需要吃掉4個果子,其中必
須包含最大的果子。即N=8,M=2,K=4:
圖一描述了果樹的形態,圖二描述了最優策略。
大頭吃4個果子,用實心點標識;
小頭吃4個果子,用空心點標識;
九頭龍的難受值為4,因為圖中用細邊標
記的樹枝被大頭吃掉了。
輸入描述 Input Description
輸入檔案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<=105),表示存在一段難受值為c的樹枝串連果子a和果子b。
輸出描述 Output Description
輸出檔案dragon.out 僅有一行,包含一個整數,表示在滿足“大頭”的要求
的前提下,九頭龍的難受值的最小值。如果無法滿足要求,輸出-1。
範例輸入 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
資料範圍及提示 Data Size & Hint
該範例對應於題目描述中的例子。
【題目分析】
大意就是:一條九頭龍的動物, 有M個腦袋, 每個腦袋都必須吃到果子, 一棵有N個果子的樹, 分配給它每個頭吃,
其中一個最大的頭要吃K個果子, 其餘分配給其它的頭, 如果一個頭同時吃到相鄰的果子會有一個難受
值, 現在要你分配果子使得難受值的和最小.
解題思路:
無解好判斷,N-K<M-1則無解(果子不夠吃)。
有解的情況
如果能把M個腦袋簡化成一個腦袋吃K個的最小代價就好辦了。分兩種情況:
1、M=2,就是大頭吃掉的樹枝+小頭吃掉的樹枝。
2、M>2,此時只需考慮大頭吃掉的樹枝,因為其他的可以根據奇偶分給不同的頭吃,從而使他們不被算入最終答案。
然後就是一個簡單的樹形DP題,先把樹轉成二叉樹,然後:f[i][j][k]=min{f[lc[i]][X][0]+f[rc[i]][j-X][k]+(m==2)*(k==0)*d[i] || f[lc[i]][X-1][1]+f[rc[i]][j-X][k]+(k==1)*d[i] }f[i][j][k]指以i為根的子樹分j個給大頭吃,父親是k(k=1被大頭吃k=0被小頭吃)。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN=302;const int INF=100000000;int n,m,k,tot=0,ans=INF;int a[MAXN][MAXN],sum[MAXN];int f[MAXN][MAXN][3];bool b[MAXN];struct node{ int lc,rc,f,d;}tree[MAXN];void debug(int v){ printf("%d\n",v); if (tree[v].lc) debug(tree[v].lc); if (tree[v].rc) debug(tree[v].rc);}void debug2(int v){ if (tree[v].lc) debug(tree[v].lc); printf("%d\n",v); if (tree[v].rc) debug(tree[v].rc);}int dp(int root,int x,int g){ if (x<0) return INF; if (f[root][x][g]>=0) return f[root][x][g]; if (!root && !x) return f[root][x][g]=0; f[root][x][g]=INF; for (int i=0;i<=min(x,sum[root]);i++) { int t1=dp(tree[root].lc,i,0)+(m==2)*(g==0)*tree[root].d; int t2=dp(tree[root].lc,i-1,1)+(g==1)*tree[root].d; int t3=dp(tree[root].rc,x-i,g); t1=min(t1,t2); f[root][x][g]=min(f[root][x][g],t1+t3); } return f[root][x][g];}void Find(int root){ if (!root) return ; Find(tree[root].lc); Find(tree[root].rc); sum[root]=sum[tree[root].lc]+sum[tree[root].rc]+1;}int main(){ scanf("%d%d%d",&n,&m,&k); for (int i=1;i<n;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); tree[y].rc=tree[x].lc; tree[x].lc=y; tree[y].d=d; } if (n-k<m-1) {printf("-1\n");return 0;} memset(sum,0,sizeof(sum)); Find(1); memset(f,255,sizeof(f)); printf("%d\n",dp(tree[1].lc,k-1,1)); return 0;}
codevs貪吃的九頭龍