HAOI2010软件安装(树形背包)

HAOI2010软件安装(树形背包)

题意

  有n个物品,每个物品最多会依赖一个物品,但一个物品可以依赖于一个不独立(依赖于其它物品)的物品,且可能有多个物品依赖一个物品,并且依赖关系可能形成一个环。现给你V的资金,问如何分配资金,可以使你的得到的总价值最大,请求出这个总价值。

解法

  我以前写过对于普通依赖性背包的博客:noip2006金明的预算方案如果对依赖性背包不是很熟悉的同学可以先看一下这道题。

由于这道题的依赖关系可能形成环,所以我们先用tarjan缩一下点,然后依赖关系图就变成了一个森林,这时候我们再将每一棵树的根节点向0号结点连一条边,表示他们依赖0号结点。这时候我们就得到了一颗依赖关系树。

那么现在的重点就在如何处理这颗依赖关系树。因为树上每一个节点的决策数都太大了,所以同样的我们考虑求出每一个以i节点为根的子树在任意权值下的最大价值,然后再在i节点利用01背包来合并,一直递归往上。最后的答案就是0号结点所有方案中最优的那一个。

ps:如果还是看不懂的话,可以参观这里这里我也是在那儿学的。

代码

以下的代码是 \(O(nv^2)\) 的:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <vector>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
    x=0;T k=1;char c=getchar();
    while(!isdigit(c)){if(c==‘-‘)k=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-‘0‘;c=getchar();}x*=k;
}
const int maxn=2500+15;
int w[maxn],cost[maxn];
int n,m,ecnt;
struct Edge{
    int u,v;
    Edge(int u,int v):u(u),v(v){}
};
vector<Edge> edge;
vector<int> G[maxn];
void add_edge(int u,int v) {
    edge.push_back(Edge(u,v));
    ecnt=edge.size();
    G[u].push_back(ecnt-1);
}

int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
bool ins[maxn];

void tarjian(int u) {
    ins[u]=1;sta[++top]=u;
    dfn[u]=low[u]=++cnt;
    for(int i=0;i<G[u].size();i++) {
        Edge e=edge[G[u][i]];
        int v=e.v;
        if(!dfn[v]) {
            tarjian(v);
            low[u]=min(low[u],low[v]);
        }
        else if(ins[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
        int y;
        while((y=sta[top--])&&y) {
            bl[y]=u;
            ins[y]=0;
            if(u==y) break;
            w[u]+=w[y],cost[u]+=cost[y];
        }
    }
}
int dp[maxn][maxn];
void dfs(int u) {
    dp[u][cost[u]]=w[u];
    for(int i=0;i<G[u].size();i++) {
        Edge e=edge[G[u][i]];
        int v=e.v;
        dfs(v);
        for(int i=m;i>=cost[u];i--)
            for(int j=0;j<=i-cost[u];j++)
            dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
    }
}
bool noroot[maxn];
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;i++) read(cost[i]);
    for(int i=1;i<=n;i++) read(w[i]);
    for(int i=1;i<=n;i++) {
        int d;read(d);
        if(!d) continue;
        add_edge(d,i);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
    int k=edge.size();
    for(int i=0;i<=n;i++) G[i].clear();

    for(int i=0;i<k;i++) {
        Edge e=edge[i];
        int f1=bl[e.u],f2=bl[e.v];
        if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!!
        add_edge(f1,f2);noroot[f2]=1;
    }

    for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
    dfs(0);
    int ans=0;
    for(int i=0;i<=m;i++)
        ans=max(ans,dp[0][i]);
    printf("%d\n",ans);
    return 0;
}

这下面的是参照徐持横的算法做到的 \(O(nv)\) 的算法:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <vector>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
    x=0;T k=1;char c=getchar();
    while(!isdigit(c)){if(c==‘-‘)k=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-‘0‘;c=getchar();}x*=k;
}
const int maxn=500+15;
int w[maxn],cost[maxn];
int n,m,ecnt;
struct Edge{
    int u,v;
    Edge(int u,int v):u(u),v(v){}
};
vector<Edge> edge;
vector<int> G[maxn];
void add_edge(int u,int v) {
    edge.push_back(Edge(u,v));
    ecnt=edge.size();
    G[u].push_back(ecnt-1);
}

int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
bool ins[maxn];

void tarjian(int u) {
    ins[u]=1;sta[++top]=u;
    dfn[u]=low[u]=++cnt;
    for(int i=0;i<G[u].size();i++) {
        Edge e=edge[G[u][i]];
        int v=e.v;
        if(!dfn[v]) {
            tarjian(v);
            low[u]=min(low[u],low[v]);
        }
        else if(ins[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
        int y;
        while((y=sta[top--])&&y) {
            bl[y]=u;
            ins[y]=0;
            if(u==y) break;
            w[u]+=w[y],cost[u]+=cost[y];
        }
    }
}
int dp[maxn][maxn];
void dfs(int u,int M) {
    if(M<=0) return;
    for(int i=0;i<G[u].size();i++) {
        Edge e=edge[G[u][i]];
        int v=e.v;
        for(int j=1;j<=M;j++) dp[v][j]=dp[u][j];
        dfs(v,M-cost[v]);
        for(int j=cost[v];j<=M;j++) dp[u][j]=max(dp[u][j],dp[v][j-cost[v]]+w[v]);
    }
}
bool noroot[maxn];
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;i++) read(cost[i]);
    for(int i=1;i<=n;i++) read(w[i]);
    for(int i=1;i<=n;i++) {
        int d;read(d);
        if(!d) continue;
        add_edge(d,i);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
    int k=edge.size();
    for(int i=0;i<=n;i++) G[i].clear();

    for(int i=0;i<k;i++) {
        Edge e=edge[i];
        int f1=bl[e.u],f2=bl[e.v];
        if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!!
        add_edge(f1,f2);noroot[f2]=1;
    }

    for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
    dfs(0,m);
    int ans=0;
    for(int i=0;i<=m;i++) ans=max(ans,dp[0][i]);
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/mrasd/p/9562489.html

时间: 2024-10-11 13:02:18

HAOI2010软件安装(树形背包)的相关文章

【BZOJ2427】[HAOI2010]软件安装 Tarjan+树形背包

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

P2515 [HAOI2010]软件安装

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

BZOJ2427: [HAOI2010]软件安装

2427: [HAOI2010]软件安装 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1512  Solved: 584[Submit][Status][Discuss] Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j

BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP

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

[Bzoj 2427] [HAOI2010] 软件安装 tarjan缩点+树形DP

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

HAOI2010 软件安装 有依赖的背包DP

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

洛谷 P2515 [HAOI2010]软件安装

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

cogs 444. [HAOI2010]软件安装

★★☆   输入文件:install.in   输出文件:install.out   简单对比 时间限制:1 s   内存限制:128 MB [问题描述]现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大).但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j).幸运的 是,一个软件最

洛谷—— P2515 [HAOI2010]软件安装

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