BZOJ2427: [HAOI2010]软件安装

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

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;
}

 

posted @ 2018-10-31 20:18  符拉迪沃斯托克  阅读(159)  评论(0编辑  收藏  举报
Live2D