贪吃的九头龙-----树形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;

}








 

 

你可能感兴趣的:(dp)