[Bzoj 2427] [HAOI2010] 软件安装

2427: [HAOI2010]软件安装

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2011  Solved: 802
[Submit][Status][Discuss]

Description

现在我们的手头有 N 个软件,对于一个软件 i ,它要占用 Wi 的磁盘空间,它的价值为 Vi 。我们希望从中选择一些软件安装到一台磁盘容量为 M 计算机上,使得这些软件的价值尽可能大(即 Vi 的和最大)。

但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件 j(包括软件 j 的直接或间接依赖)的情况下才能正确工作(软件 i 依赖软件 j )。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为 0 。

我们现在知道了软件之间的依赖关系:软件i依赖软件 Di 。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则 Di=0 ,这时只要这个软件安装了,它就能正常工作。

Input

   第1行:N, M  (0<=N<=100, 0<=M<=500)
      第2行:W1, W2, ... Wi, ..., Wn (0<=Wi<=M )
      第3行:V1, V2, ..., Vi, ..., Vn  (0<=Vi<=1000 )
      第4行:D1, D2, ..., Di, ..., Dn(0<=Di<=N, Di≠i )

Output

一个整数,代表最大价值。

Solution

观察到,对于一个环上的一群节点,要么都选,要么都不选。(如果选部分不会发生价值,相当于白白浪费空间)

所以先 Tarjan 缩点,然后树形 DP 即可。(我好菜啊还不会树形 DP )

Code

// By YoungNeal
#include<cstdio> #include<iostream> using namespace std; int c[105]; bool in[105]; int deg[105]; int n,m,root; int cnt,sum,tot; int f[105][505]; int stk[105],top; int low[105],dfn[105]; int head[105],head2[105]; int w[105],v[105],d[105]; int scc_w[105],scc_v[105]; struct Edge{ int to,nxt; }edge[10005],edge2[10005]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } void add_c(int x,int y){ edge2[++cnt].to=y; edge2[cnt].nxt=head2[x]; head2[x]=cnt; } void tarjan(int now){ dfn[now]=low[now]=++sum; stk[++top]=now;in[now]=1; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(!dfn[to]) tarjan(to),low[now]=min(low[now],low[to]); else if(in[to]) low[now]=min(low[now],low[to]); } if(dfn[now]==low[now]){ tot++;int y; do{ y=stk[top--]; c[y]=tot; in[y]=0; scc_w[tot]+=w[y]; scc_v[tot]+=v[y]; }while(y!=now); } } void dfs(int now){ for(int i=head2[now];i;i=edge2[i].nxt){ int to=edge2[i].to; dfs(to); for(int j=m;~j;j--){ for(int p=0;p<=j;p++) f[now][j]=max(f[now][j],f[to][p]+f[now][j-p]); } } for(int i=m;~i;i--){ if(i>=scc_w[now]) f[now][i]=f[now][i-scc_w[now]]+scc_v[now]; else f[now][i]=0; } } signed main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++) scanf("%d",&d[i]); for(int i=1;i<=n;i++){ if(!d[i]) continue; add(d[i],i); } cnt=0; for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } for(int i=1;i<=n;i++){ if(!d[i]) continue; if(c[i]==c[d[i]]) continue; add_c(c[d[i]],c[i]);deg[c[i]]++; } tot++; for(int i=1;i<tot;i++){ if(deg[i]) continue; add_c(tot,i); } dfs(tot); printf("%d\n",f[tot][m]); return 0; }

 

posted @ 2018-03-26 23:12  YoungNeal  阅读(200)  评论(0编辑  收藏  举报