洛谷 P2015 二叉苹果树

题目传送门

树形dp,(f_{i,j})表示以i为根,子树删j条边的最少苹果数

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

int n,q,head[101],si[101],m,pp,ans,ss[101],ll[101],oo[101];
int f[101][101];
struct kkk {
	int to,next,v;
}e[500];

inline void add(int x,int y,int vv) {
	e[++pp].to = y;
	e[pp].v = vv;
	e[pp].next = head[x];
	head[x] = pp;
	oo[y]++;
}

inline int dp(int rt,int fa,int le) {
	f[rt][0] = 0;
	if(e[head[rt]].to == fa && oo[rt] == 1) {
		f[rt][1] = le;
		si[rt] = 1;
		return 1;
	}
	int sum = 0,len = 0,so[2],tot = -1;
	for(int i = head[rt];i; i = e[i].next) {
		int u = e[i].to;
		if(u == fa) continue;
		si[rt] += dp(u,rt,e[i].v);
		so[++tot] = u;
	}
	si[rt]++;
	for(int i = 1;i <= min(m,si[rt] - 1); i++)
		for(int k = 0;k <= 1; k++)//二叉树,两个儿子 
			for(int j = 0;j <= si[so[k]]; j++)
				//因为一共删i条,所以左子树删j条,那右子树就删i-j条 
				f[rt][i] = min(f[rt][i],f[so[k]][j] + f[so[k^1]][i-j]);
	if(m >= si[rt]) f[rt][si[rt]] = ll[rt];
	return si[rt];
}

inline int dfs(int rt,int fa,int le) {
	for(int i = head[rt];i; i = e[i].next) {
		int u = e[i].to;
		if(u == fa) continue;
		ll[rt] += dfs(u,rt,e[i].v);
	}
	return ll[rt] = ll[rt] + le;
}

int main() {
	memset(f,0x3f3f3f,sizeof(f));
	scanf("%d%d",&n,&q);
	m = n - q - 1;
	for(int i = 1;i < n; i++) {
		int x,y,t;
		scanf("%d%d%d",&x,&y,&t);
		add(x,y,t);
		add(y,x,t);
		ans += t;
	}
	dfs(1,0,0);
	dp(1,0,0);
	printf("%d",ans - f[1][m]);
	//总数减去最少去除量 
	return 0;
}
原文地址:https://www.cnblogs.com/lipeiyi520/p/13663886.html