bzoj 1017 : [JSOI2008]魔兽地图DotR

比较难想的的一道树形dp。

看到这道题正常的思路应该是$f[i][j][k]$表示i这棵子树里买了j个i物品花费为k的最大收益。

但如果直接这么定义的话转移复杂度会很高,需要枚举j,枚举孩子,枚举k,枚举孩子的花费,还要枚举每个孩子各买了多少件。

想办法把最后一个循环去掉。

重新定义状态$f[i][j][k]$表示表示i这棵子树里至少买了j个i物品花费为k的最大收益。

每次枚举完物品数量后加上这么一句

if(i!=l[x])f[x][i][j]=max(f[x][i][j],f[x][i+1][j]);

l[x]为x的最大数量。

相当于一个后缀最大值。

就可以轻松转移了。

虽然复杂度还是有些高。(貌似存在复杂度更低的算法?)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define inf 0x3f3f3f3f
 6 using namespace std;
 7 int n,m;
 8 int f[55][110][2005];
 9 int g[55][2005];
10 int v[55],cost[55],l[55];
11 int head[55],ver[110],nxt[110],tot,quan[110];
12 void add(int a,int b,int c)
13 {
14     tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;quan[tot]=c;return ;
15 }
16 bool vis[55];
17 void dfs(int x)
18 {
19     if(!head[x])
20     {
21         l[x]=min(l[x],m/cost[x]);
22         for(int i=0;i<=l[x];i++)
23         {
24             for(int j=0;j<=i;j++)
25             {
26                 f[x][j][i*cost[x]]=i*v[x];
27             }
28         }
29         return ;
30     }
31     l[x]=inf;
32     for(int i=head[x];i;i=nxt[i])
33     {
34         dfs(ver[i]);
35         l[x]=min(l[x],l[ver[i]]/quan[i]);
36         cost[x]+=cost[ver[i]]*quan[i];
37     }
38     l[x]=min(l[x],m/cost[x]);
39     memset(g,0xcf,sizeof(g));
40     g[0][0]=0;
41     for(int i=l[x];i>=0;i--)
42     {
43         int cnt=0;
44         for(int j=head[x];j;j=nxt[j])
45         {
46             cnt++;
47             for(int k=0;k<=m;k++)
48             {
49                 for(int l=0;l<=k;l++)
50                 {
51                     g[cnt][k]=max(g[cnt][k],g[cnt-1][l]+f[ver[j]][i*quan[j]][k-l]-v[ver[j]]*(i*quan[j]));
52                 }
53             }
54         }
55         for(int j=0;j<=m;j++)
56         {
57             f[x][i][j]=g[cnt][j]+i*v[x];
58             if(i!=l[x])f[x][i][j]=max(f[x][i][j],f[x][i+1][j]);
59         }
60     }
61 
62     return ;
63 }
64 int main()
65 {
66     scanf("%d%d",&n,&m);
67     memset(f,0xcf,sizeof(f));
68     for(int i=1;i<=n;i++)
69     {
70         scanf("%d",&v[i]);
71         char s[2];scanf("%s",s);
72         if(s[0]=='A')
73         {
74             int num;
75             int t1,t2;
76             scanf("%d",&num);
77             for(int j=1;j<=num;j++)
78             {
79                 scanf("%d%d",&t1,&t2);
80                 vis[t1]=1;
81                 add(i,t1,t2);
82             }
83         }
84         else
85         {
86             scanf("%d%d",&cost[i],&l[i]);
87         }
88     }
89     int root=0;
90     for(int i=1;i<=n;i++)
91     {
92         if(!vis[i])add(0,i,1);
93     }
94     dfs(root);
95     int ans=0;
96     for(int i=0;i<=m;i++)ans=max(ans,f[root][0][i]);
97     printf("%d\n",ans);
98     return 0;
99 }

 

posted @ 2017-03-21 09:12  SD_le  阅读(270)  评论(0编辑  收藏  举报
重置按钮