[BZOJ]3090: Coci2009 [podjela] 树形DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/86100904

Description

有 N 个农民, 他们住在 N 个不同的村子里. 这 N 个村子形成一棵树.
每个农民初始时获得 X 的钱.
每一次操作, 一个农民可以从它自己的钱中, 取出任意数量的钱, 交给某个相邻村子的农民.

对于每个农民给定一个值 v_i, 求
(1) 最少需要多少次操作, 使得每个农民最终拿到的钱 >= 给定的值.

Solution

这种题都被出烂了……但是我还是思考了挺久,写了挺久。
就是状态设计要体现贪心, f i , j f_{i,j} 表示以 i i 为根子树,至少使除了 i i 外的所有点满足条件,操作了 j j 次最多向上贡献多少钱,转移就是个树形背包,这样按 s i z e size 合并相当于枚举点对,因为每对点只会在 L C A LCA 处被枚举到,所以复杂度为 n 2 n^2

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=2010;
const int inf=1044266559;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void upd(int&x,int y){x=max(x,y);}
int n,X,v[Maxn],f[Maxn][Maxn],sz[Maxn],g[Maxn],h[Maxn];//f[i][j]操作j次最多向上贡献多少 
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
	int t=++len;
	e[t].y=y;e[t].next=last[x];last[x]=t;
}
void dfs(int x,int fa)
{
	bool flag=true;sz[x]=1;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa)continue;
		dfs(y,x);flag=false;
	}
	if(flag)
	{
		if(v[x]<0)f[x][0]=v[x];
		else f[x][0]=0,f[x][1]=v[x];
		return;
	}
	memset(g,-63,sizeof(g));
	bool fir=true;
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa)continue;
		if(fir)
		{
			fir=false;
			for(int j=0;j<=sz[y];j++)
			{
				if(f[y][j]==-inf)continue;
				if(f[y][j]<0)g[j+1]=f[y][j];
				else g[j]=f[y][j];
			}
		}
		else
		{
			for(int j=0;j<=sz[x]+sz[y];j++)h[j]=-inf;
			for(int j=0;j<=sz[x];j++)
			{
				if(g[j]==-inf)continue;
				for(int k=0;k<=sz[y];k++)
				{
					if(f[y][k]==-inf)continue;
					upd(h[j+k+(f[y][k]<0)],g[j]+f[y][k]);
				}
			}
			for(int j=0;j<=sz[x]+sz[y];j++)g[j]=h[j];
		}
		sz[x]+=sz[y];
	}
	for(int j=0;j<=sz[x];j++)
	{
		if(g[j]==-inf)continue;
		if(g[j]+v[x]>=0)upd(f[x][j],0),upd(f[x][j+1],g[j]+v[x]);
		else upd(f[x][j],g[j]+v[x]);
	}
}
int main()
{
	memset(f,-63,sizeof(f));
	n=read(),X=read();
	for(int i=1;i<=n;i++)v[i]=X-read();
	for(int i=1;i<n;i++){int x=read(),y=read();ins(x,y),ins(y,x);}
	dfs(1,0);
	for(int i=0;i<=n;i++)if(f[1][i]>=0)return printf("%d",i),0;
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/86100904