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 }