题意不多说。
曰:
呃,首先显然这是一个树形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
原文地址:http://blog.csdn.net/vmurder/article/details/41379009