BZOJ2427 软件安装 : Tarjan缩点 + 树形背包
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
一个整数,代表最大价值。
Sample Input
3 10
5 5 6
2 3 4
0 1 1
Sample Output
5
HINT
题解
这是一个有依赖的背包问题,但题目中依赖关系可能会形成环,就代表如果要取这个物品就得把这个强连通分量的所有物品给取完。
所以可以把其缩成一个物品,再重新建立依赖关系,从被依赖指向依赖,用依赖背包求解即可。
其中f[i][j]代表再以i个物品为根时,体积为j时的最优方案。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#include<vector>
#define MAXN 100+10
#define MAXM 500+10
using namespace std;
struct Edge{
int next,from,to,tnext;
};
struct G{
int head[MAXN],num,tail[MAXN];
Edge edge[MAXM*2];
void add(int from,int to)
{
edge[++num].next=head[from];
edge[num].tnext=tail[to];
edge[num].from=from;
edge[num].to=to;
head[from]=num;tail[to]=num;
}
}g1,g2;
struct Data{
int w,v;
}k[MAXN];
int v[MAXN],w[MAXN],f[MAXN][MAXM];
int n,m;
int low[MAXN],dfn[MAXN],dfnum,cnt[MAXN],col[MAXN],co,vis[MAXN],vis2[MAXN];
int ans[MAXN],ans2[MAXN],maxn;
stack<int> st;
vector<int> con[MAXN];
void tarjan(int x)
{
dfn[x]=low[x]=++dfnum;vis[x]=1;st.push(x);
for(int i=g1.head[x];i;i=g1.edge[i].next)
{
if(!dfn[g1.edge[i].to])
{
tarjan(g1.edge[i].to);
low[x]=min(low[x],low[g1.edge[i].to]);
}else if(vis[g1.edge[i].to]) low[x]=min(low[x],dfn[g1.edge[i].to]);
}
if(low[x]==dfn[x])
{
int t=1,v1=0,w1=0;col[x]=++co;vis[x]=0;
con[co].push_back(x);
while(st.top()!=x)
{
con[co].push_back(st.top());t++;
vis[st.top()]=0;
col[st.top()]=co;
v1+=v[st.top()];
w1+=w[st.top()];
st.pop();
}
v1+=v[st.top()];
w1+=w[st.top()];
st.pop();cnt[co]=t;
k[co].w=w1;k[co].v=v1;
}
}
int ru[MAXN];
void dp(int x)
{
for(int i=g2.head[x];i;i=g2.edge[i].next)
{
dp(g2.edge[i].to);
for(int j=m-k[x].w;j>=0;j--)
for(int l=0;l<=j;l++) //l代表给x的子树g2.edge[i].to分配j-l空间时最优方案。
f[x][j]=max(f[x][j],f[x][l]+f[g2.edge[i].to][j-l]);
for(int i=m;i>=0;i--)
{
if(i>=k[x].w) f[x][i]=f[x][i-k[x].w]+k[x].v;
else f[x][i]=0;
}
}
int 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++)
{
int x;scanf("%d",&x);
if(x) g1.add(x,i);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(col[g1.edge[i].from]!=col[g1.edge[i].to])
g2.add(col[g1.edge[i].from],col[g1.edge[i].to]),
ru[col[g1.edge[i].to]]++;
for(int i=1;i<=co;i++)
if(ru[i]==0) g2.add(co+1,i);
k[co+1].v=k[co+1].w=0;
dp(co+1);
printf("%d\n",f[co+1][m]);
return 0;
}