[bzoj2427] [HAOI2010] 软件安装(强连通分量)(树形背包DP)
Posted oi-zzyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj2427] [HAOI2010] 软件安装(强连通分量)(树形背包DP)相关的知识,希望对你有一定的参考价值。
题干:
现在我们的手头有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)。
还有一种打法:先加上现节点的权值,再进行比较,求最大值。当现在节点所枚举到的体积与子节点所枚举到的体积相等时,即未选现节点的权值时,需要特殊处理,防止更新出类似完全背包一样的重复的情况。
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\n",ans);
99
100
以上是关于[bzoj2427] [HAOI2010] 软件安装(强连通分量)(树形背包DP)的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ2427][HAOI2010]软件安装(Tarjan+DP)