[NOI2002 贪吃的九头龙]

[关键字]:树形DP

[题目大意]:N个节点的一棵树被M个脑袋吃,每个脑袋至少吃一个。最大的头必须恰好吃K个且必须包括1号节点。如果一条树边的两边都是被同一个脑袋吃掉的,则这段树枝的权值将被计算进答案中,要求使答案最小。

//====================================================================================================================================================

[分析]:无解好判断,N-K<M-1则无解(果子不够吃)。但一看非无解情况M个脑袋分N个点,其中一个还必须分得K个且包括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被小头吃)。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=302;
const int INF=100000000;

struct node
{
int lc,rc,f,d;
}tree[MAXN];
int N,M,K,tot=0,ans=INF;
int a[MAXN][MAXN],sum[MAXN];
int f[MAXN][MAXN][3];
bool b[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);
}

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

void Init()
{
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;
}
memset(sum,0,sizeof(sum));
Find(1);
//for (int i=1;i<=N;i++) printf("%d %d\n",i,sum[i]);
//for (int i=1;i<=N;i++) printf("%d %d\n",tree[i].lc,tree[i].rc);
//debug(1);
//printf("=====================================\n");
//debug2(1);
}

int dp(int root,int x,int g)
{
// if (!root) return 0;
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);
}
//printf("%d %d %d %d\n",root,x,g,f[root][x][g]);
return f[root][x][g];
}

void Solve()
{
if (N-K<M-1) {printf("-1\n");return ;}
memset(f,255,sizeof(f));
printf("%d\n",dp(tree[1].lc,K-1,1));
}

int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
Init();
Solve();
return 0;
}



你可能感兴趣的:([NOI2002 贪吃的九头龙])