贪吃的九头龙-----树形dp

开始切树形dp。。。

思路:

经研究发现,当m大于3的时候,可以把m当成3来运算。

先把多叉树转化成二叉树,方便寻找每个节点的儿子和兄弟。

dp[i][j][k]: 以i节点为根节点的子树,有j个大头,节点i的状态为k,这时候的难受值。(k=0,1)当k=0时,代表小头吃这个节点,反之,大头吃。

map[i][j]: 节点i到节点j的边长。

dp[x][j][1]=min(dp[x][j][1],dp[y][k][1]+tmp[j-k][1]+map[x][y],dp[y][k][0]+tmp[j-k][1]);

dp[x][j][0]=min(dp[x][j][0],dp[y][k][0]+tmp[j-k][0]+t               ,dp[y][k][1]+tmp[j-k][0]);

tmp[i][j]: 在运算一个节点之前,已经运算过的节点形成的dp[x][i][j];

t代表当一条边两边都是0时,所应该增加的难受值。



 

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
struct list
{
	int l;
	int r;
}node[1001];
int n,m,need;
int map[301][301];
int min3(int a,int b,int c)
{
	if(a>b)a=b;
	if(a>c)a=c;
	return a;
}
void init()
{
	int i,a,b,c;
	cin>>n>>m>>need;
	for(i=1;i<=n;i++)
	{
		node[i].l=node[i].r=0;
	}
	for(i=0;i<n-1;i++)
	{
		cin>>a>>b>>c;
		if(a>b)swap(a,b);
		map[a][b]=c;
		if(node[a].l==0)
		{
			node[a].l=b;
		}
		else
		{
			a=node[a].l;
			while(node[a].r)
			{
				a=node[a].r;
			}
			node[a].r=b;
		}
	}
}
int sum[301];
int dp[301][301][10];
int tmp[301][10];
void dfs(int x)
{
	dp[x][1][1]=0;
	dp[x][0][0]=0;
	int j,k;
	sum[x]=1;
	int i;
	i=node[x].l;
	while(i)
	{
		int y=i;
		dfs(y);
		int t;
		t=0;
		if(m==2)t=map[x][y];
		sum[x]+=sum[y];
		for(j=0;j<301;j++)
		{
			tmp[j][0]=dp[x][j][0];
			tmp[j][1]=dp[x][j][1];
		}
		memset(dp[x],0x2f,sizeof(dp[x]));
		for(j=sum[x];j>=0;j--)
		{
			for(k=j-1;k>=0;k--)dp[x][j][1]=min3(dp[x][j][1],dp[y][k][0]+tmp[j-k][1],dp[y][k][1]+tmp[j-k][1]+map[x][y]);
			for(k=j;k>=0;k--) dp[x][j][0]=min3(dp[x][j][0],dp[y][k][1]+tmp[j-k][0],dp[y][k][0]+tmp[j-k][0]+t);
		}
		i=node[i].r;
	}
}
int main()
{
	init();
	if(n<m+need-1)
	{
		printf("-1\n");
		return 0;
	}
	memset(dp,0x2f,sizeof(dp));
	dfs(1);
	cout<<dp[1][need][1];
	return 0;
}




 

 

posted @ 2013-05-21 20:52  javawebsoa  Views(462)  Comments(0Edit  收藏  举报