BZOJ2427: [HAOI2010]软件安装
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
5 5 6
2 3 4
0 1 1
Sample Output
5
题解Here!
最近沉迷$DP$无法自拔。。。
这是一个有依赖的背包问题。
强行$Tarjan$缩点然后转换成树形$DP$。
缩点应该不用多说。
然后来看树上怎么做。
设$dp[i][j]$表示当前在$i$这个点,用了$j$个单位的磁盘空间,所能获得的最大价值。
转移方程就很好写啦:
设$weight[i]$表示$i$这个点的磁盘空间,$value[i]$表示$i$这个点的价值。
首先对于$DFS$到的所有的$u$,我们初始化:
$$dp[u][i]=value[u],i\in[weight[u],m]$$
对于$\forall v\in son_u$,我们这样转移:
$$dp[u][i+weight[u]]=\max\{\ dp[v][j]+dp[u][i+weight[u]-j]\ |\ i\in[0,m-weight[u]],j\in[0,i]\ \}$$
其中,$i$为倒序枚举,$j$为正序枚举。
这就是树形背包全过程。
但是!缩完点之后有可能不止一棵树啊!
不要紧,我们建立一个超级树根$root$,连到每棵树的树根即可。
最终答案就是$dp[root][m]$。
然而一开始把缩点敲炸了,尴尬。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 110
using namespace std;
int n,m,root;
int weight[MAXN],value[MAXN],colour[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
namespace Tarjan{//强行namespace,缩点
int c=1,d=1,top=1,s=0;
int cstack[MAXN],head[MAXN],deep[MAXN],low[MAXN],w[MAXN],v[MAXN],fa[MAXN];
bool vis[MAXN];
struct Edge{
int next,to;
}a[MAXN];
inline void add_edge(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
}
void dfs(int x){
deep[x]=low[x]=d++;
vis[x]=true;
cstack[top++]=x;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(!deep[v]){
dfs(v);
low[x]=min(low[x],low[v]);
}
else if(vis[v])low[x]=min(low[x],deep[v]);
}
if(low[x]==deep[x]){
s++;
do{
colour[cstack[top-1]]=s;
vis[cstack[top-1]]=false;
}while(cstack[--top]!=x);
}
}
void solve(){
for(int i=1;i<=n;i++)w[i]=read();
for(int i=1;i<=n;i++)v[i]=read();
for(int i=1;i<=n;i++){
fa[i]=read();
if(fa[i])add_edge(fa[i],i);
}
for(int i=1;i<=n;i++)if(!deep[i])dfs(i);
for(int i=1;i<=n;i++){
weight[colour[i]]+=w[i];
value[colour[i]]+=v[i];
}
}
}
namespace DP{//树形背包
int c=1;
int head[MAXN],deep[MAXN],indegree[MAXN],dp[MAXN][MAXN*5];
struct Tree{
int next,to;
}a[MAXN<<2];
inline void add_edge(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
}
void dfs(int rt){
int will;
for(int i=weight[rt];i<=m;i++)dp[rt][i]=value[rt];
for(int i=head[rt];i;i=a[i].next){
will=a[i].to;
if(!deep[will]){
deep[will]=deep[rt]+1;
dfs(will);
for(int j=m-weight[rt];j>=0;j--){
for(int k=0;k<=j;k++){
dp[rt][j+weight[rt]]=max(dp[rt][j+weight[rt]],dp[rt][j+weight[rt]-k]+dp[will][k]);
}
}
}
}
}
void solve(){
root=n+1;
weight[root]=value[root]=0;
for(int i=1;i<=n;i++){
int v=Tarjan::fa[i];
if(!v)continue;
if(colour[i]!=colour[v]){
add_edge(colour[v],colour[i]);
indegree[colour[i]]++;
}
}
for(int i=1;i<=Tarjan::s;i++)if(!indegree[i])add_edge(root,i);
deep[root]=1;
dfs(root);
}
}
void work(){
DP::solve();
printf("%d\n",DP::dp[root][m]);
}
void init(){
n=read();m=read();
Tarjan::solve();
}
int main(){
init();
work();
return 0;
}

浙公网安备 33010602011771号