BZOJ 2427: [HAOI2010]软件安装

17 篇文章 0 订阅
10 篇文章 0 订阅

Description

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

Solution
这道题蛮好==
首先构图一发。
然后发现有环。
有环的话环可以简单粗暴地看成一个物体。
所以我们先跑一边强联通,然后树形dp
最后分组背包,AC
一开始树形dp敲错了==天生对这种dp有恐惧不知为何。。
想必是儿时收到过心理阴影==

Code

/**************************************************************
    Problem: 2427
    User: bblss123
    Language: C++
    Result: Accepted
    Time:120 ms
    Memory:1512 kb
****************************************************************/

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int M=105;
int dp[M][M*5],n,m;
#define vec vector<int>
#define pb push_back
vec G[M],edge[M];
int wvf[M][3];
int sccid[M],pre[M],lowlink[M],allc,dfs_clock;
int w[M],val[M],fa[M];
stack<int>stk;
inline void Min(int &a,int b){if(a>b)a=b;}
bool flag[M];
bool predfs(int v){
    pre[v]=lowlink[v]=++dfs_clock;
    stk.push(v);
    flag[v]=1;
    for(int i=0;i<edge[v].size();++i){
        int to=edge[v][i];
        if(!pre[to])predfs(to),Min(lowlink[v],lowlink[to]);
        else if(!sccid[to])Min(lowlink[v],pre[to]);
    }
    if(lowlink[v]==pre[v]){
        sccid[v]=++allc;
        for(;!stk.empty();){
            int k=stk.top();stk.pop();
            sccid[k]=allc;
            val[allc]+=wvf[k][1];
            w[allc]+=wvf[k][0];
            if(k==v)break;
        }
    }
}
bool mark[M][M];
inline void Graph(){
    for(int i=1;i<=n;++i){
        int v=sccid[i];
        for(int j=0;j<edge[i].size();++j){
            int to=sccid[edge[i][j]];
            if(to==v)continue;
            if(!mark[v][to])G[v].pb(to),fa[to]=v;
        }
    }
}
inline void Max(int &a,int b){if(a<b)a=b;}
void dfs(int v){
    for(int i=0;i<G[v].size();++i)
        dfs(G[v][i]);
    int k=m-w[v];
    for(int j=0;j<G[v].size();++j){
        int to=G[v][j];
        for(int i=k;i>=0;--i)
            for(int l=i;~l;l--)
                Max(dp[v][i],dp[v][l]+dp[to][i-l]);
    }
    for(int i=1;i<=k;++i)
        Max(dp[v][i],dp[v][i-1]);
    for(int i=k;i>=0;--i)
        dp[v][i+w[v]]=dp[v][i]+val[v],dp[v][i]=0;
}
vec que;
int ans[M*5];
int main(){
    scanf("%d %d",&n,&m);
    for(int j=0;j<=2;++j)
        for(int i=1;i<=n;++i){
            scanf("%d",&wvf[i][j]);
            if(j==2&&wvf[i][j])edge[wvf[i][j]].pb(i);
        }
    for(int i=1;i<=n;++i)
        if(!flag[i])predfs(i);
    Graph();
    for(int i=1;i<=allc;++i)
        if(!fa[i])que.pb(i),dfs(i);
    for(int i=0;i<que.size();++i){
        int v=que[i];
        for(int k=m;~k;--k)
            for(int j=k;~j;--j)
                Max(ans[k],dp[v][j]+ans[k-j]);
    }//WA?
    int res=0;
    for(int i=0;i<=m;++i)
        Max(res,ans[i]);
    printf("%d\n",res);
    return 0;
}

其实跑完强连通以后可以不用树形dp+分组背包,可以直接对节点进行讨论,进行一个类似01背包的dp,这样就可以在最后讨论的时候省掉一个n,况且代码长度也可以大大缩短

因为没有去想所以没想到也是很正常的事情

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值