背包问题

背包问题本质上是求取全组合问题,可做的选择在外循环,遍历的范围在内循环

一. 0-1背包问题

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
该类型题目的特点是:每种物品仅有一件,可以选择放或不放。直接回溯搜索的时间复杂度非常大,必须使用动态规划求解

dp[i][j]表示前i件物品放入一个容量为j的背包可以获得的最大价值
从而可以实现状态的转移,并保证无后效性

for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
    for(int j=V;j>=0;j--)//从后往前遍历可容纳空间
      dp[i][j]=max{dp[i-1][v],dp[i-1][j-c[i]]+w[i]}

空间一维优化

for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
    for(int j=V;j>=0;j--)//遍历可容纳空间
      dp[j]=max{dp[v],dp[j-c[i]]+w[i]} //空间一维优化

C++模板

int maxval(int V,vector<int>&w,vector<int>&c){
    int n = w.size();
    vector<int> dp(V+1);
    int res = 0;
    for(int i=0;i<n;i++)
        for(int j=V;j>=c[i];j--){
            dp[j] = max(dp[j],dp[j-c[i]]+w[i]);
            res = max(res,dp[j]);
        }
    return res;
}

1.1. 采药

1.2. 装箱问题

1.3. 小A点菜

1.4. 精卫填海

1.5. 5倍经验日

1.6. 最大约数和

1.7. 考前临时抱佛脚

1.8. Subset Sum

1.9. U盘

1.10. 书架

二. 完全背包问题

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。

for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
    for(int j=0;j<=V;j++)//从前往后遍历可容纳空间,遍历过程中,可以将同一物品选择多次
      dp[i][j]=max{dp[i-1][j],dp[i-1][j-c[i]]+w[i]}

C++模板

int maxval(int v,vector<int>&c,vector<int>&w){ 
    int n = w.size();
    vector<int> dp(v+1);
    int res = 0;
    for(int i=0;i<n;i++)
        for(int j=c[i];j<=v;j++){
            dp[j] = max(dp[j],dp[j-c[i]]+w[i]);
            res = max(res,dp[j]);
        }
    return res;
}

2.1. 疯狂的采药

三. 多重背包问题

有N种物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。第i种物品最多有n[i]件可用
其实将第i件物品拆成n[i]件物品,就转化成了0-1背包问题
如果n[i]值过大的话会使得时间复杂度过大,这里可以使用二进制技巧,将拆分的物品价值和费用皆转化为1,2,4,8
从而保证可以凑出0~n[i]中的任意数值,同时使得时间复杂度为对数级别

3.1. 砝码称重

四.混合背包问题

有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)

for i=1..N
    if 第i件物品属于01背包
        ZeroOnePack(c[i],w[i])
    else if 第i件物品属于完全背包
        CompletePack(c[i],w[i])
    else if 第i件物品属于多重背包
        MultiplePack(c[i],w[i],n[i])

//0-1背包
procedure ZeroOnePack(cost,weight)
    for v=V..cost
        f[v]=max{f[v],f[v-cost]+weight}

//完全背包
procedure CompletePack(cost,weight)
    for v=cost..V
        f[v]=max{f[v],f[v-c[i]]+w[i]}

//多重背包
procedure MultiplePack(cost,weight,amount)
    if cost*amount>=V  //对应数目的容量超过上限,相当于无限多
        CompletePack(cost,weight)
        return
    integer k=1
    while k<amount  //否则转化为多个0-1背包问题,二进制拆解
        ZeroOnePack(k*cost,k*weight)
        amount=amount-k
        k=k*2
    ZeroOnePack(amount*cost,amount*weight)

4.1. 樱花

五. 二维背包问题

对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价,通常另一种费用是个数限制

f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

C++模板

int maxval(int v,int m,vector<int>&weight,vector<int>&volume,vector<int>&w){ 
    int n = w.size();
    int dp[v+1][m+1];
    memset(dp,0,sizeof(dp));
    int res = 0;
    for(int i=0;i<n;i++)
        for(int j=v;j>=volume[i];j--)
            for(int k=m;k>=weight[i];k--){
                dp[j][k] = max(dp[j][k],dp[j-volume[i]][k-weight[i]]+w[i]);
                res = max(res,dp[j][k]);
            }
    return res;
}

5.1. NASA的食物计划

六. 分组背包问题

N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件
相比之前的问题,做选择不再是单个物品,而是分组

for 所有的组k
    for v=V..0 
        for 所有的i属于组k
            f[v]=max{f[v],f[v-c[i]]+w[i]}

6.1. 通天之分组背包

posted @ 2023-08-19 03:33  失控D大白兔  阅读(6)  评论(0编辑  收藏  举报