树形背包+Tarjan缩点 [HAOI2010]软件安装(洛谷 P2515)

[HAOI2010]软件安装

题目描述

现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为MM计算机上,使得这些软件的价值尽可能大(即Vi​的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件ii依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要软件安装了,它就能正常工作。

输入格式

第1行:N,M(0≤N≤100,0≤M≤500)

第2行:W_1,W_2, … W_i, …, W_n (0≤Wi≤M)

第3行:V_1, V_2, …, V_i, …, V_n (0≤Vi≤1000)

第4行:D_1, D_2, …, D_i, …, D_n ( D_i≠i,0≤Di ≤N)

输出格式

一个整数,代表最大价值


这题和选课的最大差别在于这道题可能成环,并且每个物品都有体积和价值两个属性;

Tarjan缩完点后直接树形背包就行了;

dp部分代码有点区别,因为要保证父节点必须被选,所以 j+w[p];

for(int j=m-w[p];j>=0;j--){
			for(int k=0;k<=j;k++){
				dp[p][j+w[p]]=max(dp[p][j+w[p]],dp[p][j+w[p]-k]+dp[q][k]);
			}
		}	

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=11000;
const int M=1000100;
const LL mod=100000000;
int dfn[N],low[N],tot,head[N],cnt,n,m,sta[N],top,fa[N];
int w[N],v[N],d[N],in[N],dp[N][1100];
bool vis[N];
struct Node{
	int to,nex;
}edge[M];
void add(int p,int q){
	edge[cnt].to=q;
	edge[cnt].nex=head[p];
	head[p]=cnt++;
}
void Tarjan(int p){
	dfn[p]=low[p]=++tot;
	if(!vis[p]) vis[p]=true,sta[++top]=p;
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		if(!dfn[q]){
			Tarjan(q);
			low[p]=min(low[p],low[q]);
		}
		else if(vis[q]) low[p]=min(low[p],dfn[q]);
	}
	if(dfn[p]==low[p]){
		fa[p]=p;
		vis[p]=false;
		while(sta[top]!=p){
			vis[sta[top]]=false;
			fa[sta[top]]=p;
			w[p]+=w[sta[top]];
			v[p]+=v[sta[top]];
			top--;
		}
		top--;
	}
}
void dfs(int p){
	for(int i=w[p];i<=m;i++) dp[p][i]=v[p];
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		dfs(q);
		for(int j=m-w[p];j>=0;j--){
			for(int k=0;k<=j;k++){
				dp[p][j+w[p]]=max(dp[p][j+w[p]],dp[p][j+w[p]-k]+dp[q][k]);
			}
		}	
	}
}
int main(){
	memset(head,-1,sizeof(head));
	cin>>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]) add(d[i],i);
	} 
	for(int i=1;i<=n;i++){
		if(!dfn[i]) Tarjan(i);
	}
	cnt=0;//重新建边,相当于缩点 
	memset(head,-1,sizeof(head));
	memset(edge,0,sizeof(edge));
	for(int i=1;i<=n;i++){
		if(d[i]&&fa[i]!=fa[d[i]]){
			add(fa[d[i]],fa[i]);
			in[fa[i]]++;
		}
	}
	for(int i=1;i<=n;i++) if(!in[fa[i]]&&fa[i]==i) add(0,fa[i]);
	dfs(0);
	cout<<dp[0][m]<<endl;
    return 0;
}

发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/105055575