P2015 二叉苹果树

发布时间 2023-07-12 15:06:17作者: pig_pig

原题链接戳这里

思考过程

一眼树状dp+背包dp
每一根树枝占用 1 空间 带来的价值由题目输入

设计 f[u][i] 表示在考虑以 u 为根的子树时 分配给它 i 根树枝 所能达到的最大价值

于是在以 u 为根的子树中想要新拓展一个以 v 为根的子树时

有转移方程 f[u][i]=max(f[u][i],f[u][i-k-1]+f[v][k]+w)
——由 u 至 v ,总共空间为 i ,分给以 v 为根的子树空间为 k
是否选择加入以 v 为根的子树 为 01 背包 注意倒序循环

这里的 i-k-1 是因为由 u 至 v 本身就需要连接一条边

初版代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define N 115
#define il inline
#define Inf 0x3f3f3f3f

int n,m;
int x,y,z;
int sz[N];
int f[N][N];

struct node{
	int to,next,w;
}tr[N<<1];
int cnt,head[N];

il int read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			f=0;
	for(;isdigit(ch);ch=getchar())
		x=(x<<1)+(x<<3)+ch-'0';
	return f?x:(~(x-1));
}

void add(int u,int v,int w)
{
	tr[++cnt].to=v;
	tr[cnt].w=w;
	tr[cnt].next=head[u];
	head[u]=cnt;
}

void dfs(int u,int fa)
{
	for(int i=head[u];i!=-1;i=tr[i].next)
	{
		int v=tr[i].to;
		if(v==fa)continue;
//		cout<<u<<"->"<<v<<endl;
		dfs(v,u);
		for(int j=m;j>=1;j--)
		{
			for(int k=j-1;k>=0;k--)
			{
				
				f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+tr[i].w);
			}
		}
	}
}

int main()
{
	n=read();m=read();
	for(int i=0;i<=n;i++)head[i]=-1;
	for(int i=1;i<n;i++)
	{
		x=read();y=read();z=read();
		add(x,y,z);
		add(y,x,z);
	}
	dfs(1,0);
	cout<<f[1][m];
}
 

一些似乎有用的小优化

对着转移过程仔细思考一下

for(int j=m;j>=1;j--)
{
	for(int k=j-1;k>=0;k--)
	{	
		f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+tr[i].w);
	}
}

这里的 j 是为以 u 为根的子树留出的空间
而 k 是为以 v 为根的子树留出的空间

那么有没有一种可能 这些子树根本没有那么多树枝来占用空间呢

于是可以在 dfs 的过程中统计子树中的树枝数量
在 dp转移 过程中就可以减小一些时间复杂度了(也许微不足道?)

最终代码(节选 dfs 部分)

点我查看代码喔~
void dfs(int u,int fa)
{
	for(int i=head[u];i!=-1;i=tr[i].next)
	{
		int v=tr[i].to;
		if(v==fa)continue;
//		cout<<u<<"->"<<v<<endl;
		dfs(v,u);
        sz[u]+=sz[v]+1;//在这里统计了树枝数量
		for(int j=min(m,sz[u]);j>=1;j--)
		{
			for(int k=min(sz[v],j-1);k>=0;k--)
			{
				
				f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+tr[i].w);
			}
		}
	}
}