BZOJ 2427: [HAOI2010]软件安装

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 2119 Solved: 850
[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, …, 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

解题思路

应该很好看出这是一道树上背包问题,但是光跑树上背包只能拿40分,是因为有可能出现环状,
就是几个点相互依赖,要选必须全选,所以我们考虑tarjan缩点,大点的体积等于所有点加起来,
权值同样。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 105;
const int MAXM = 505;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

struct Edge{
    int nxt,to;
}edge[MAXN];

int n,m,v[MAXN],w[MAXN],d[MAXN];
int dp[MAXN][MAXM],cost[MAXN],val[MAXN];
int head[MAXN],cnt,tot,dis[MAXN][MAXN];
int dfn[MAXN],low[MAXN],sta[MAXN],top,col[MAXN],num;
bool vis[MAXN],used[MAXN];

inline void add(int bg,int ed){
    edge[++cnt].to=ed;
    edge[cnt].nxt=head[bg];
    head[bg]=cnt;
}

inline void Tarjan(int x){
    dfn[x]=low[x]=++tot;
    vis[x]=1;
    sta[++top]=x;
    for(register int i=head[x];i;i=edge[i].nxt){
        int u=edge[i].to;
        if(!dfn[u]){
            Tarjan(u);
            low[x]=min(low[x],low[u]);
        }
        else if(vis[u])
            low[x]=min(low[x],dfn[u]);
    }
    if(low[x]==dfn[x]){
        vis[x]=0;
        col[x]=++num;
        while(sta[top]!=x){
            col[sta[top]]=num;
            vis[sta[top--]]=0;
        }
        top--;
    }
}

inline int DP(int x){
    for(register int i=cost[x];i<=m;i++) dp[x][i]=val[x];
    for(register int i=head[x];i;i=edge[i].nxt){
        int u=edge[i].to;
        DP(u);
        for(register int j=m-cost[x];j>=0;j--)
            for(register int k=0;k<=j;k++)
                dp[x][j+cost[x]]=max(dp[x][j+cost[x]],dp[x][j+cost[x]-k]+dp[u][k]);
    }   
}

int main(){
    n=rd();m=rd();
    for(register int i=1;i<=n;i++) w[i]=rd();
    for(register int i=1;i<=n;i++) v[i]=rd();
    for(register int i=1;i<=n;i++) {
        d[i]=rd();
        add(d[i],i);
    }
    for(register int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
//  for(register int i=1;i<=n;i++) cout<<col[i]<<" ";
    for(register int i=1;i<=n;i++){
        cost[col[i]]+=w[i];val[col[i]]+=v[i];
//      cout<<col[i]<<" "<<col[d[i]]<<endl;
        for(register int j=head[i];j;j=edge[j].nxt)
            if(col[i]!=col[edge[j].to])
                dis[col[i]][col[edge[j].to]]=1,used[col[edge[j].to]]=1;
    }
    memset(head,0,sizeof(head));cnt=0;
    for(register int i=1;i<=num;i++)
        for(register int j=1;j<=num;j++)
            if(dis[i][j]) add(i,j);
    for(register int i=1;i<=num;i++) if(!used[i])
        add(num+1,i);
    DP(num+1);
//  for(register int i=0;i<=num;i++) cout<<dp[n][i]<<" ";
    printf("%d\n",dp[num+1][m]);
}

猜你喜欢

转载自blog.csdn.net/qq_40448823/article/details/80736610