[HAOI2010]软件安装(Tarjan,树形dp)
[HAOI2010]软件安装
题目描述
现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
输入输出格式
输入格式:
第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 )
输出格式:
一个整数,代表最大价值
输入输出样例
输入样例#1:
3 10
5 5 6
2 3 4
0 1 1
输出样例#1:
5
被看错题和脑抽卡了一晚上......
就是个有依赖的树形背包,但是没看到间接依赖也不能对答案造成贡献,以为像是没有上司的舞会那样的dp。
先将原图缩点,要选的话至少要选一个强连通分量,否则不能对答案造成贡献,缩完点之后就是个DAG了,直接跑树形背包就完了。(我居然连树形背包的板子都不记得了)
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
const int N=110,M=510;
int n,m,cnt,top,visnum,num,ans;
int w[N],c[N],d[N],sumw[N],sumc[N],head[N];
int dfn[N],low[N],s[N],in[N],belong[N],du[N];
int dp[N][M];
struct node{
int to,next;
}edge[2*N];
void add(int x,int y){
cnt++;edge[cnt].to=y;edge[cnt].next=head[x];head[x]=cnt;
}
queue<int>q;
void tarjan(int k){
dfn[k]=low[k]=++visnum;
s[++top]=k;in[k]=1;
for(int i=head[k];i;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);low[k]=min(low[k],low[v]);
}
else if(in[v])low[k]=min(low[k],dfn[v]);
}
if(dfn[k]==low[k]){
num++;int v=0;
while(v!=k){
v=s[top--];in[v]=0;
belong[v]=num;sumw[num]+=w[v];sumc[num]+=c[v];
}
}
}
void DP(int k){
for(int i=head[k];i;i=edge[i].next){
int v=edge[i].to;DP(v);
for(int j=m;j>=0;j--)
for(int l=j;l>=0;l--)
dp[k][j]=max(dp[k][j],dp[k][j-l]+dp[v][l]);
}
for(int i=m;i>=0;i--)
if(i>=sumw[k])
dp[k][i]=dp[k][i-sumw[k]]+sumc[k];
else dp[k][i]=0;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++)w[i]=read();
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++){
d[i]=read();if(d[i])add(d[i],i);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
cnt=top=0;memset(head,0,sizeof(head));
for(int i=1;i<=n;i++){
if(!d[i]||belong[i]==belong[d[i]])continue;
add(belong[d[i]],belong[i]);du[belong[i]]++;
}
for(int i=1;i<=num;i++)if(!du[i])add(0,i);
DP(0);for(int i=0;i<=m;i++)ans=max(ans,max(dp[0][i],dp[0][i]));
printf("%d",ans);return 0;
}