Luogu3354 河流 - 树形dp -

题目链接:https://www.luogu.com.cn/problem/P3354

题解:
考虑树形dp
\(f[i][j][k]\)表示考虑到 i 点,j 是i的祖先中与i最近的伐木场,i及子树中共有 k 个伐木场的最小代价
我们发现,这样设状态无法表示 i 是否有伐木场这个条件,因此钦定 f[][][] 表示 i 没有伐木场,g[][][]表示有

转移发现就是一个树形背包。
如果是 f[][][]的话,最后别忘加上 i 点 到j的贡献
值得一提的是合并答案,注意到i点处理完之后i点是否有伐木场已经不重要了(注意i的子树也已经处理完了),因此可以把 g[][][] 合并到 f[][][],使代码更简洁

// by Balloons
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn=105;

struct node{
	int to, w;
	node(){}
	node(int to,int w) : to(to), w(w){}
};
int n,m;
vector<node>ve[maxn];
int w[maxn], de[maxn], dep[maxn];
int anc[maxn], sz = 0;
int f[maxn][maxn][maxn], g[maxn][maxn][maxn];

void dfs(int x,int fat = -1){
	anc[++ sz] = x;
	for(node nd : ve[x]){
		int u = nd.to, w = nd.w;
		if(u == fat)continue;
		dep[u] = dep[x] + w;
		dfs(u, x);
		for(int i=1;i<=sz;i++){
			int j = anc[i];
			for(int k = m;k>=0;k--){
				f[x][j][k] += f[u][j][0];
				g[x][j][k] += f[u][x][0];
				
				for(int p = 0;p<=k;p++)
					f[x][j][k] = min(f[x][j][k], f[x][j][p] + f[u][j][k - p]);
				for(int p = 1;p<=k;p++)
					g[x][j][k] = min(g[x][j][k], g[x][j][p] + f[u][x][k - p]);
			}
		}
	}
	
	for(int i = 1;i<=sz;i++){
		int j = anc[i];
		for(int p = 0;p<=m;p++)
			if(p == 0)f[x][j][p] += w[x] * (dep[x] - dep[j]);
			else f[x][j][p] = min(f[x][j][p] + w[x] * (dep[x] - dep[j]), g[x][j][p]);
	}
	-- sz;
}

signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		int fa;
		scanf("%d%d%d",&w[i],&fa,&de[i]);
		ve[i].push_back(node(fa, de[i]));
		ve[fa].push_back(node(i, de[i]));
	}
	dfs(0);
	printf("%d\n",f[0][0][m]);

	return 0;
}


posted @ 2022-09-10 13:59  SkyRainWind  阅读(21)  评论(0编辑  收藏  举报