#树形dp#洛谷 3354 河流

题目

一颗有N+1个结点的树,树中的每个结点可能会生产出一些产品。这些产品要么就地加工(要有加工厂才行),要么运送到它的父亲结点那儿去。现在在整棵树的根结点处已经有了一个产品加工厂,而且所有的产品最终必须在某个加工厂加工才行。由于运费昂贵,不可能将所有的产品都运送到根节点处加工。现在决定在树中的某些结点新增总共K个加工厂,现在要你选择这K个加工厂的厂址。


分析

树形dp,用兄弟孩子表示法,
f [ x ] [ i + j ] [ p ] = m i n ( f [ x ] [ i + j ] [ p ] , f [ s o n 1 ] [ i ] [ p + 1 ] + f [ s o n 2 ] [ j ] [ p ] + w [ x ] ∗ d i s [ x ] [ p ] ) f[x][i+j][p]=min(f[x][i+j][p],f[son1][i][p+1]+f[son2][j][p]+w[x]*dis[x][p]) f[x][i+j][p]=min(f[x][i+j][p],f[son1][i][p+1]+f[son2][j][p]+w[x]dis[x][p])
f [ x ] [ i + j + 1 ] [ p ] = m i n ( f [ x ] [ i + j + 1 ] [ p ] , f [ s o n 1 ] [ i ] [ 1 ] + f [ s o n 2 ] [ j ] [ p ] ) f[x][i+j+1][p]=min(f[x][i+j+1][p],f[son1][i][1]+f[son2][j][p]) f[x][i+j+1][p]=min(f[x][i+j+1][p],f[son1][i][1]+f[son2][j][p])


代码

#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
int n,k,w[102],fa[102],d[102],f[102][102][102],bro[102],son[102],dis[102][102];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int min(int a,int b){return (a<b)?a:b;}
void dp(int x,int limi){
	int x1=son[x],x2=bro[x];
	for (int i=1;i<=limi;i++) dis[x][i]=dis[fa[x]][i-1]+d[x];
	if (x1) dp(x1,limi+1); if (x2) dp(x2,limi);
	for (int i=0;i<=n;i++)
	for (int j=0;j<=n-i;j++)
	for (int p=1;p<=limi;p++)
	f[x][i+j][p]=min(f[x][i+j][p],f[x1][i][p+1]+f[x2][j][p]+w[x]*dis[x][p]),
	f[x][i+j+1][p]=min(f[x][i+j+1][p],f[x1][i][1]+f[x2][j][p]);
}
int main(){
	n=in(); k=in();
	for (int i=1;i<=n;i++){
		w[i]=in(); fa[i]=in(); d[i]=in();
		bro[i]=son[fa[i]]; son[fa[i]]=i;
	}
	memset(f,0x7f,sizeof(f));
	for (int i=0;i<=n;i++)
	for (int j=0;j<=n;j++) f[0][i][j]=0;
	dp(son[0],1); return !printf("%d",f[son[0]][k][1]); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值