【题意】

2427: [HAOI2010]软件安装

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 929  Solved: 371
[ Submit][ Status][ Discuss]

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, ..., Wn0<=Wi<=M
      3行:V1, V2, ..., Vi, ..., Vn 0<=Vi<=1000
      4行:D1, D2, ..., Di, ..., Dn0<=Di<=N, Di≠i

Output

一个整数,代表最大价值。

Sample Input

3 10
5 5 6
2 3 4
0 1 1

Sample Output

5
【解题方法】

观察样例容易知道,这是一堆点套着一个环,由于是有向图,可以缩点,缩点之后就是一个树形dp模板了,缩点不会,还特意去学了一下,可以参考这篇blog:点击打开链接

【AC 代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 105;
int n,m,tot,scc,ind,top;
int v[maxn],w[maxn],sv[maxn],sw[maxn];
int q[maxn],in[maxn*5];bool inq[maxn];
int low[maxn],dfn[maxn],belong[maxn];
int dp[maxn][maxn*5];
int head1[maxn],head2[maxn];
struct edge{
    int to,next;
}e1[maxn*5],e2[maxn*5];
void init1(){
    memset(head1,0,sizeof(head1));
    tot=0;
}
void init2()
{
    memset(head2,0,sizeof(head2));
    tot=0;
}
void addedge1(int u,int v){
    e1[++tot].to=v,e1[tot].next=head1[u],head1[u]=tot;
}
void addedge2(int u,int v){
    in[v]=1;
    e2[++tot].to=v,e2[tot].next=head2[u],head2[u]=tot;
}
void tarjian(int x)
{
    int now=0;
    low[x]=dfn[x]=++ind;
    q[++top]=x;inq[x]=1;
    for(int i=head1[x]; i; i=e1[i].next){
        int v=e1[i].to;
        if(!dfn[v]){
            tarjian(v);
            low[x]=min(low[x],low[v]);
        }else if(inq[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(low[x]==dfn[x]){
        scc++;
        while(now!=x){
            now=q[top--];inq[now]=0;
            belong[now]=scc;
            sv[scc]+=v[now];
            sw[scc]+=w[now];
        }
    }
}
void rebuild(){
    tot=0;
    for(int x=1; x<=n; x++)
    for(int i=head1[x]; i; i=e1[i].next)
        if(belong[e1[i].to]!=belong[x])
            addedge2(belong[x],belong[e1[i].to]);
}
void dfs(int x){
    for(int i=head2[x]; i; i=e2[i].next){
        int v=e2[i].to;
        dfs(v);
        for(int j=m-sw[x]; j>=0; j--){
            for(int k=0; k<=j; k++){
                dp[x][j] = max(dp[x][j],dp[x][k]+dp[v][j-k]);
            }
        }
    }
    for(int j=m; j>=0; j--){
        if(j>=sw[x]) dp[x][j]=dp[x][j-sw[x]]+sv[x];
        else dp[x][j]=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]);
    init1();
    for(int i=1; i<=n; i++){
        int x;
        scanf("%d",&x);
        if(x) addedge1(x,i);
    }
    for(int i=1; i<=n; i++){
        if(!dfn[i]) tarjian(i);
    }
    //init2();
    rebuild();
    for(int i=1; i<=scc; i++){
        if(!in[i]) addedge2(scc+1,i);
    }
    dfs(scc+1);
    cout<<dp[scc+1][m]<<endl;
    return 0;
}