bzoj 1812

什么鬼noip互测题...

这题很显然是树形dp,但设计状态以及转移是个难点

记状态f[i][j][k]表示以i为根节点的子树,离i最近的祖宗节点编号为j放了虫洞(伐木场?),i的子树内放了k个伐木场的方案数

设to为i的某个子节点,当i不放伐木场时,有:

dp[i][j][k]=min(dp[to][j][k-c]+dp[i][j][c])

当i放伐木场时,有:

dp[i][i][k]=min(dp[to][i][k-c]+dp[i][i]c])

最后合并:

dp[i][j][k]+=num[i]*dis[i][j]

dp[i][j][k]=min(dp[i][j][k],dp[i][i][k])

其中dis[i][j]表示从i到j(j为i的某个祖先节点)的距离,可以预处理出来

特别的,当i为叶节点(即i没有子节点)时,dp[i][j][0]=dis[i][j]*num[i]直接赋值

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
int dp[205][205][55];
struct Edge
{
	int next;
	int to;
}edge[205];
int head[205];
int dis[205][205];
int num[205];
int f[205];
int n,k;
int cnt=1;
void init()
{
	memset(head,-1,sizeof(head));
	memset(f,-1,sizeof(f));
	cnt=1;
}
void add(int l,int r)
{
	edge[cnt].next=head[l];
	edge[cnt].to=r;
	head[l]=cnt++;
}
void initdfs(int x)
{
	if(x)
	{
		for(int i=f[x];i!=-1;i=f[i])
		{
			dis[x][i]=dis[x][f[x]]+dis[f[x]][i];
		}
		dis[x][0]=dis[x][f[x]]+dis[f[x]][0];
	}
	for(int i=head[x];i!=-1;i=edge[i].next)
	{
		int to=edge[i].to;
		initdfs(to);
	}
}
void dfs(int x)
{
	if(head[x]==-1)
	{
		for(int i=f[x];i!=-1;i=f[i])
		{
			dp[x][i][0]=dis[x][i]*num[x];
		}
		return;
	}
	if(x)
	{
		dp[x][x][0]=0x3f3f3f3f;
	}
	for(int i=head[x];i!=-1;i=edge[i].next)
	{
		int to=edge[i].to;
		dfs(to);
		for(int j=f[x];j!=-1;j=f[j])
		{
			for(int t=k;t>=0;t--)
			{
				int temp=0x3f3f3f3f;
				for(int c=0;c<=t;c++)
				{
					temp=min(temp,dp[x][j][c]+dp[to][j][t-c]);
				}
				dp[x][j][t]=temp;
			}
		}
		for(int j=k;j>=0;j--)
		{
			int temp=0x3f3f3f3f;
			for(int c=(x!=0);c<=j;c++)
			{
				temp=min(temp,dp[x][x][c]+dp[to][x][j-c]);
			}
			dp[x][x][j]=temp;
		}
	}
	for(int i=f[x];i!=-1;i=f[i])
	{
		for(int j=0;j<=k;j++)
		{
			dp[x][i][j]+=dis[x][i]*num[x];
			dp[x][i][j]=min(dp[x][i][j],dp[x][x][j]);
		}
	}
}
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int main()
{
//	freopen("girls.in","r",stdin);
//	freopen("girls.out","w",stdout);
	n=read(),k=read();
	init();
	f[0]=-1;
	for(int i=1;i<=n;i++)
	{
		int v;
		num[i]=read(),f[i]=read(),v=read();
		dis[i][f[i]]=v;
		add(f[i],i);	
	}
	initdfs(0);
	dfs(0);
	printf("%d\n",dp[0][0][k]);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值