码迷,mamicode.com
首页 > 其他好文 > 详细

【East!模拟赛】【Round1】【BZOJ1017】魔兽地图Dotr 树形DP

时间:2014-11-22 12:08:48      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:bzoj1017   树形dp   

题意不多说。

曰:

    呃,首先显然这是一个树形DP。

    然后我不会什么高大上的算法,我只能提供一种非常慢,但是能在BZ切的算法(因为是总时限并且还有O2)。

题解:

    不需要新建树,这个树就行。

    首先这道题一眼就能想到树形背包。。但是树形背包怎么做呢?

    因为需要合成,所以这里的状态F[i][j][k]并不是i节点有j个花费为k时的最大收益,而是i节点给其父亲j个花费k时的最大收益(不含父亲收益)。好吧,这个我自己没想出来,但是或许我可以给你讲讲怎么想出来。

    我们考虑这道题有两个难点:

    第一个是怎么确定父亲买多少个,第二个是怎么保证所有子节点都贡献了这么多个。。

    其实我们不用想太多,这种题我们可以在时间复杂度尽量允许的前提下暴力一些,然后剪枝。

    当然,这道题我并没有剪枝。

---------废话说了好多。

    我们想到父亲需要占用一些子节点,那么子节点的状态中一定就有跟个数相关的内容,所以我们可以记录低级物品有j个的状态,可以这样一来我们枚举需要多少个,然后?好吧,没有然后了,即便是暴力,这也太暴力了。

    所以想到能不能记录父亲有多少个呢?那么就有了F[i][j][k]为i节点给其父亲j个花费k时的最大收益(不含父亲收益)的状态。

    而枚举父亲有多少个的时候只需要加这个东西就行了。

    但是又出现了一个问题,就是枚举到父亲i个时需要每一种物品都有i个贡献,这个很难处理。

    这里就涉及到了一个小技巧:for循环每到一个子节点时都记录当前父亲的状态,然后f清成-1,这时再更新。

有点累,不想细说了。。自己看代码应该可以领悟(-1的意义、最终价格的正确性两点)。

    好了,说完了。

贴代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 60
#define P 110
#define M 2010
#define inf 0x3f3f3f3f
using namespace std;
struct KSD
{
	int v,len,next;
}e[N];
int head[N],cnt;
void add(int u,int v,int len)
{
	++cnt;
	e[cnt].v=v;
	e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
int n,m,root,ans;
int f[N][P][M],g[M];
int strength[N],cost[N],num[N];

bool in[N],out[N];
char opt;

void Tree_DP(int x)
{
	int i,j,k,v,temp;
	if(!out[x])
	{
		num[x]=min(num[x],m/cost[x]);
		for(i=0;i<=num[x];i++)
			for(j=i;j<=num[x];j++)
				f[x][i][j*cost[x]]=(j-i)*strength[x];
		return ;
	}
	num[x]=inf;
	for(i=head[x];i;i=e[i].next)
	{
		v=e[i].v;
		Tree_DP(v);
		num[x]=min(num[x],num[v]/e[i].len);
	}
	for(i=0;i<=num[x];i++)f[x][i][0]=0;
	for(i=head[x];i;i=e[i].next)
	{
		v=e[i].v;
		for(j=0;j<=num[x];j++)
		{
			memcpy(g,f[x][j],sizeof(f[x][j]));
			memset(f[x][j],-1,sizeof(f[x][j]));
			for(k=m;k>=0;k--)
			{
				for(temp=k;temp>=0;temp--)
					if(g[k-temp]+1&&f[v][j*e[i].len][temp]+1)
						f[x][j][k]=max(f[x][j][k],g[k-temp]+f[v][j*e[i].len][temp]);
			}
		}
	}
	for(i=0;i<=num[x];i++)
		for(j=i;j<=num[x];j++)
			for(k=0;k<=m;k++)
				if(f[x][j][k]+1)
					f[x][i][k]=max(f[x][i][k],f[x][j][k]+(j-i)*strength[x]),
					ans=max(ans,f[x][i][k]);
	return ;
}

int main()
{
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	int i,j,k;
	int a,b;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d %s",&strength[i],&opt);
		if(opt=='A')
		{
			scanf("%d",&k);
			for(j=1;j<=k;j++)
			{
				scanf("%d%d",&a,&b);
				add(i,a,b);
				in[a]=1;
				out[i]=1;
			}
		}
		if(opt=='B')scanf("%d%d",&cost[i],&num[i]);
	}
	memset(f,-1,sizeof(f));
	for(i=1;i<=n;i++)if(!in[i]){root=i;break;}
	Tree_DP(root);
	printf("%d\n",ans);

	fclose(stdin);
	fclose(stdout);
	return 0;
}


【East!模拟赛】【Round1】【BZOJ1017】魔兽地图Dotr 树形DP

标签:bzoj1017   树形dp   

原文地址:http://blog.csdn.net/vmurder/article/details/41379009

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!