题干:
现在我们的手头有N个软件,对于一个软件 i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即 Vi 的和最大)。但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件 i 依赖软件 j )。幸运的 是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一 次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
题解:
先看一下题面,体积?空间?那肯定是背包啊。于是作者便高高兴兴地打出状态转移方程:
dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+v[x]) (错解)
于是喜提一0分。。。
本题正解其实就是背包dp,又因为题干中说 一个软件最多依赖另外一个软件(每个节点最多只有一个出度) ,那应该就是一个树上dp(作者表示连这是一棵树也要看不出来了)。
于是作者又高高兴兴地打出状态转移方程(分组背包模型):
dp[i][j]=ΣiL=0 ΣjR=0 max(dp[L][R])+v[x] (正解)
于是又喜提一0分。。。
考虑一下:每个节点最多只有一个出度,它就一定是一棵树吗?显然不是。这就需要我们用 tarjan 缩点来解决(合并权值与体积为一点,重新建图)。
交上代码,又双是0分。。。
最终其实错在了:在最终加上现节点的权值时(上面更新的是找贡献最大的子节点权值和),需要将体积小于 现节点的体积 的情况归零(我们上面更新的就是 给现节点的体积留有位置时的情况,针对不满足的情况就需归0)。
还有一种打法:先加上现节点的权值,再进行比较,求最大值。当现在节点所枚举到的体积与子节点所枚举到的体积相等时,即未选现节点的权值时,需要特殊处理,防止更新出类似完全背包一样的重复的情况。
有依赖的dp就和分组dp很像,我们可以把没有依赖的物品称主件,有依赖的称为附件,本题是在树上,那么一定要定义给以i为根的子树分配 vi 的空间所能达到的最大价值,那然后发现多个儿子转移到父亲似乎不好转移,父亲好像要给儿子分配时间。
重新考虑一下,对于一个主件对应的几个附件可以看作是决策集合,那么这个集合在子树中有指数级多的的决策,怎么办呢?如果我们优化的话,对于每一个决策,我们要找相同体积找价值最大的,这是显然的,然后从子树转移过来的时候,我们会发现所谓的dp[i][v]就是 vi 体积下对应的那个最大的价值,决策已经保证最优,只要循环 vi 转移即可,道理同上。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #define $ 110
4 using namespace std;
5 int n,m,v[$],w[$],d[$],dp[$][$*5],ans,dfn[$],low[$],tar,sta[$],up,circle=1,cir[$];
6 int wc[$],vc[$],second[$],first[$],tot1,tot2,sum[$],out[$];
7 bool judge[$];
8 struct tree{ int to,next; }a[$],b[$];
9 inline int read(){
10 char a=getchar();
11 int sum=0;
12 while(a<'0'||a>'9') a=getchar();
13 while(a>='0'&&a<='9') sum=(sum<<1)+(sum<<3)+a-'0',a=getchar();
14 return sum;
15 }
16 inline int max(int x,int y){ return x>y?x:y; }
17 inline int min(int x,int y){ return x<y?x:y; }
18 inline void add(int x,int y){
19 a[++tot1]=(tree){ y,first[x] };
20 first[x]=tot1;
21 }
22 inline void addf(int x,int y){
23 b[++tot2]=(tree){ y,second[x] };
24 second[x]=tot2;
25 }
26 inline void tarjan(int x){
27 dfn[x]=low[x]=++tar;
28 sta[++up]=x;
29 judge[x]=1;
30 for(register int i=second[x];i;i=b[i].next){
31 int to=b[i].to;
32 if(!dfn[to]){
33 tarjan(to);
34 low[x]=min(low[x],low[to]);
35 }
36 else if(judge[to]) low[x]=min(low[x],dfn[to]);
37 }
38 if(dfn[x]==low[x]){
39 int tmp; ++circle;
40 do{
41 tmp=sta[up--];
42 judge[tmp]=0;
43 cir[tmp]=circle;
44 w[circle]+=wc[tmp];
45 v[circle]+=vc[tmp];
46 }while(tmp!=x);
47 }
48 }
49 inline void dfs(int x){
50 for(register int i=first[x];i;i=a[i].next){
51 int to=a[i].to;
52 dfs(to);
53 for(register int j=m;j>=0;--j){
54 for(register int k=j;k>=0;--k){
55 dp[x][j]=max(dp[x][j],dp[x][k]+dp[to][j-k]);
56 }
57 }
58 }
59 for(register int i=m;i>=w[x];--i) dp[x][i]=dp[x][i-w[x]]+v[x];
60 for(register int i=w[x]-1;i>=0;--i) dp[x][i]=0;
61 }
62 /*
63 void DP(int x)
64 {
65 for(int i=bit[x];i<=m;i++)
66 dp[x][i]=val[x];
67 for(int i=fi[x];i!=0;i=e[i].ne)
68 {
69 int y=e[i].v;
70 DP(y);
71 for(int j=m;j>=bit[x];j--)
72 {
73 int temp=dp[x][j];
74 for(int k=bit[x];k<=j-bit[y];k++)
75 {
76 if(k!=j)
77 dp[x][j]=max(dp[x][j],dp[x][k]+dp[y][j-k]);
78 else
79 dp[x][j]=max(dp[x][j],temp+dp[y][j-k]);
80 }
81 }
82 }
83 }*/
84 signed main(){
85 scanf("%d%d",&n,&m);
86 for(register int i=1;i<=n;++i) wc[i]=read();
87 for(register int i=1;i<=n;++i) vc[i]=read();
88 for(register int i=1,x;i<=n;++i) if(x=read()) addf(x,i);
89 for(register int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
90 for(register int i=1;i<=n;++i)
91 for(register int j=second[i];j;j=b[j].next){
92 int to=b[j].to;
93 if(cir[i]!=cir[to]) add(cir[i],cir[to]),out[cir[to]]++;
94 }
95 for(register int i=2;i<=circle;++i) if(!out[i]) add(1,i);
96 dfs(1);
97 for(register int i=0;i<=m;++i) ans=max(ans,dp[1][i]);
98 printf("%d
",ans);
99 }
100