[NOI2002]贪吃的九头龙

XLVI.[NOI2002]贪吃的九头龙

思路1.

\(f[i][j][k]\)表示:在以\(i\)为根的子树上有\(j\)个点是归大头吃的,并且第\(i\)个点是归第\(k\)个头吃的。

但这样做不仅复杂度高(似乎是\(O(n^5)\)?),还有个问题:无法保证每个头都至少吃了一个果子。

思路2.

\(f[i][j][0/1]\)表示:在以\(i\)为根的子树上有\(j\)个点是归大头吃的,并且第\(i\)个点 不是\((0)\)/是\((1)\)归大头吃的。

\(m=2\)时,对于一条边来说,只有一边归大头吃而另一边归小头吃时才不会有损失。证明显然。

否则,即\(m>2\),对于一条边来说,只有两边都归大头吃才会有损失。不归大头吃的地方,可以黑白染色直接造成没有任何地方有损失。

答案为\(f[1][k][1]\)。复杂度\(O(n^3)\)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,p,head[310],cnt,f[310][310][2],g[310][2];
struct node{
	int to,next,val;
}edge[610];
void ae(int u,int v,int w){
	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=w,head[v]=cnt++;
}
void dfs(int x,int fa){
	f[x][0][0]=f[x][1][1]=0;
	for(int i=head[x],y;i!=-1;i=edge[i].next){
		if((y=edge[i].to)==fa)continue;
		dfs(y,x);
		for(int j=0;j<=p;j++)for(int u=0;u<2;u++)g[j][u]=0x3f3f3f3f;
		for(int j=0;j<=p;j++)for(int k=0;k<=j;k++)for(int u=0;u<=min(j-k,1);u++)for(int v=0;v<=min(k,1);v++)g[j][u]=min(g[j][u],f[x][j-k][u]+f[y][k][v]+edge[i].val*(m==2?!(u^v):(u&v)));
		for(int j=0;j<=p;j++)for(int u=0;u<2;u++)f[x][j][u]=g[j][u];
	}
//	printf("%d:",x);for(int i=0;i<=p;i++)printf("(%d,%d)",f[x][i][0],f[x][i][1]);puts(""); 
}
int main(){
	scanf("%d%d%d",&n,&m,&p),memset(head,-1,sizeof(head)),memset(f,0x3f3f3f3f,sizeof(f));
	for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),ae(x,y,z);
	if(m+p-1>n){puts("-1");return 0;}
	dfs(1,0);
	printf("%d\n",f[1][p][1]);
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14597307.html