首页 > 其他分享 >chapter12-2-背包问题

chapter12-2-背包问题

时间:2024-03-17 22:00:13浏览次数:15  
标签:背包 int 问题 MAXN chapter12 物品 include dp

动态规划最经典并且在机试中重点考查的问题——背包问题。背包问题的变体繁多,这里主要讨论3种。

1.0-1背包

0-1背包问题描述的是,有\(n\)件物品,每件物品的重量为\(w[i]\),其价值为\(v[i]\),现在有容量为\(m\)的背包,如何选择物品使得装入背包物品的价值最大。

首先介绍求解这个问题的动态规划方法,其时间复杂度为\(O(nm)\),空间复杂度也为\(O(nm)\)。

设置一个二维数组\(dp[ ][ ]\),令\(dp[i][j]\)表示前\(i\)个物品装进容量为\(j\)的背包能获得的最大价值。那么\(dp[n][m]\)的值就是0-1背包问题的解。

0-1背包的递推式如下:
0-1背包递推式.jpg
0-1背包.jpg

点菜问题
//0-1背包问题 点菜问题 2024-03-17
#include <iostream>
#include <cstdio>

using namespace std;
const int MAXN = 100 + 10;
const int MAXM = 1000 + 10;

int weight[MAXN];
int value[MAXN];

int dp[MAXN][MAXM];


int main() {
    int c, n;//容量为c,物品共n件
    while(scanf("%d%d", &c, &n) != EOF) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &weight[i], &value[i]);
        }

        for(int i = 0; i <= n ; ++i) {
            dp[i][0] = 0;
        }
        for(int i = 0; i <= c; ++i) {
            dp[0][i] = 0;
        }
        //考虑第i件物品放入容量为j的背包
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= c; ++j) {
                if(j < weight[i]) {
                    dp[i][j] = dp[i -1][j];
                } else {
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i]);
                }
            }
        }
        //查表
        printf("%d\n",dp[n][c]);
    }
    return 0;
}

优化T__.jpg

优化空间复杂度-dp设为一维数组
//0-1背包问题 点菜问题-优化空间复杂度 2024-03-17
#include <iostream>
#include <cstdio>

using namespace std;
const int MAXN = 100 + 10;
const int MAXM = 1000 + 10;

int weight[MAXN];
int value[MAXN];

int dp[MAXM];


int main() {
    int c, n;//容量为c,物品共n件
    while(scanf("%d%d", &c, &n) != EOF) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &weight[i], &value[i]);
        }

        for(int j = 0; j <= c; ++j) {
            dp[j] = 0;
        }
        //考虑第i件物品放入容量为j的背包
        for(int i = 1; i <= n; ++i) {
            for(int j = c; j >= weight[i]; --j) {
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //查表
        printf("%d\n",dp[c]);
    }
    return 0;
}

FYI:一般采用下标为1开始存储weight和value,因为我们规定的是下标为i的话是前i件物品,那么默认下标为0的时候是什么物品都没有,是一个空集,这样可以方便我们更加好的进行处理。

2.完全背包

完全背包问题和0-1背包问题很像,二者唯一的区别就在于完全背包里面,同一件物品有无穷多件。有\(n\)件物品,每件物品的重量为\(w[i]\),其价值为\(v[i]\),每种物品的数量均为无限个,现在有容量为\(m\)的背包,如何选择物品使得装入背包物品的价值最大?

同样设置一个二维数组\(dp[MAXN][MAXM]\),令\(dp[i][j]\)表示前i个物品装进容量为j的背包能够获得的最大价值。数组\(dp[n][m]\)即为完全背包问题的解。

和0-1背包问题一样,只考虑第i件物品时,可将情况分为是否放入第i件物品两种:

完全背包.jpg

HDU 4508_减肥记
//完全背包 2024-03-17
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 100 + 10;
const int MAXM = 1e5 + 10;

int weight[MAXN];
int value[MAXN];

int dp[MAXN][MAXM];

int main() {
    int n, m;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &value[i], &weight[i]);
        }
        scanf("%d", &m);

        for(int i = 0; i <= n; ++i) {
            dp[i][0] = 0;
        }
        for(int j = 0; j <= m; ++j) {
            dp[0][j] = 0;
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                if(j < weight[i]) {
                    dp[i][j] = dp[i - 1][j]; 
                } else {
                    dp[i][j] = max(dp[i-1][j], dp[i][j - weight[i]] + value[i]);
                }
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

优化空间复杂度,请看这张图修改dp数组和递推式(也可以说是状态转移)。
完全背包优化.jpg

总之,完全背包问题的特点是每类物品可选的数量为无穷,其解法与0-1背包问题整体保持一致,与其不同的仅为状态更新时的遍历顺序。

3.多重背包

每件物品既不是只有1件,又不是无限多件,而是有限个。多重背包问题:有\(n\)种物品,每种物品的重量为\(w[i]\),其价值为\(v[i]\),每种物品的数量为\(k[i]\),现在有个容量为\(m\)的背包,如何选择使得装入背包物品的价值最大。

求解多重背包的方法就是直接转化为0-1背包问题,不过(1)简单粗暴,把有多个同一物品的每一个都看成是相同重量、相同价值的不同物品;(2)是对同种物品进行有技巧地拆分和捆绑,得到不同的组合,使得可以任意想获得几件此个物品,都能用捆绑的组合加一加得到

多重背包-1.jpg
多重背包-2.jpg

注意这个技巧性的拆分:就是类似二进制法拆,最后不满足2的幂次倍的单独捆绑为一个新物品。

HDU 2191
/*
* 题目名称:珍惜现在,感恩生活
* 题目来源:HDU 2191
* 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2191
* 代码作者:杨泽邦(炉灰)
*/

#include <iostream>
#include <cstdio>
#include <climits>

using namespace std;

const int MAXN = 100 + 10;
const int MAXM = 1e4 + 10;

int dp[MAXM];
int value[MAXN];                        //物品价值
int weight[MAXN];                       //物品重量
int amount[MAXN];                       //物品数目
int newValue[20 * MAXN];                //拆分后物品价值
int newWeight[20 * MAXN];               //拆分后物品重量

int main() {
    int caseNumber;
    scanf("%d", &caseNumber);
    while (caseNumber--) {
        int n, m;
        scanf("%d%d", &m, &n);          //n件物品,m容量的背包
        int number = 0;                 //分解后物品数量
        for (int i = 0; i < n; ++i) {
            scanf("%d%d%d", &weight[i], &value[i], &amount[i]);
            for (int j = 1; j <= amount[i]; j <<= 1) {
                newValue[number] = j * value[i];
                newWeight[number] = j * weight[i];
                number++;
                amount[i] -= j;
            }
            if (amount[i] > 0) {
                newValue[number] = amount[i] * value[i];
                newWeight[number] = amount[i] * weight[i];
                number++;
            }
        }
        for (int i = 0; i <= m; ++i) {
            dp[i] = 0;                  //初始化
        }
        for (int i = 0; i < number; ++i) {
            for (int j = m; j >= newWeight[i]; --j) {
                dp[j] = max(dp[j], dp[j - newWeight[i]] + newValue[i]);
            }
        }
        printf("%d\n", dp[m]);
    }
    return 0;
}

不知道咋,我代码AC不了,感觉逻辑没问题,拆分再重新捆绑,记录新物品的数量,眼睛快瞎了,先这样吧。

标签:背包,int,问题,MAXN,chapter12,物品,include,dp
From: https://www.cnblogs.com/paopaotangzu/p/18079010

相关文章

  • 三段论逻辑的一个问题
    据罗素报道(《逻辑与知识》,第277页),莱布尼茨试图改进三段论逻辑,把它弄得更加简洁优美。他几乎作成了一个类似于布尔逻辑的系统,但在一个关键问题上和亚里士多德发生了冲突。这让他放弃了自己的努力。他认为自己弄错了。这个问题是这样的。按照三段论逻辑的Darapti格,下列推理是有效的......
  • conda配置源问题
    6.配置源condaconfig--addchannelshttp://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/freecondaconfig--addchannelshttp://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forgecondaconfig--addchannelshttp://mirrors.tuna.tsinghua.edu.cn/anaconda/cl......
  • 处理windows下端口引发的程序运行问题
    最近用Windows10才遇到的问题在这之前我也用了很久的Win10了,却一直没有遇到过觉得有些奇葩,做下简单记录简述如果你在Windows下运行一些需要使用端口的软件,但是软件运行中发生莫名问题那么恭喜你这篇文章可能能帮助到你Windows的动态端口范围Windows中有一个......
  • 【算法与数据结构】堆排序&&TOP-K问题之深入解析二叉树(三)
    文章目录......
  • 字符串匹配/查找字符串中子串存在次数/出现位置下标 问题----- {1.[find] 2.[substr]
    下文将介绍三种方法,求解问题类型:1.子串在主串中出现次数2.子串在主串中每次出现的下标位置以此题为例:题目链接:https://www.luogu.com.cn/problem/P8195解法一:kmp#include<iostream>#include<string>usingnamespacestd;constintN=1e6+10;intne[N];......
  • Visual Studio Code中Python安装库文件遇到的问题
    不知道怎么安装库文件,在网上搜索出来好多都是VS2019版本,与现在的2023版本界面不太一样,但是还是可以通过pip安装,之前换过国内的源(现在已经忘了,果然不记录光靠脑子是不行的),用的是清华的源下载速度还可以。安装xlwt库时成功,但是安装BeautifulSoup库时报错,×Gettingrequirement......
  • 推荐系统冷启动问题概述
    冷启动问题简介冷启动问题分三类用户冷启动:如何给新用户做个性化推荐的问题物品冷启动:如何将新的物品推荐给可能对他感兴趣的用户系统冷启动:如何在一个新开发的网站上设计个性化推荐服务提供非个性化的推荐利用用户注册提供的年龄性别利用用户注册时提供的年龄,性别等个......
  • chapter12-动态规划
    动态规划:就是用于求解优化问题的方法。优化问题比如说求最大值or最小值。动态规划的做法就是采用小心地策略进行暴力搜索,在多项式时间内求得最优解。这里的小心策略就是复用子问题的解,将已解决子问题的答案保存下来,在需要子问题答案的时候便可以直接获得,而不需要重复计算,提高效......
  • keil写51遇到的奇葩问题总结
    同一代码始终编译不过,一直提示关于ds1302文件的这两个函数有问题检查了半天都没检查出来问题。最后发现是因为我D盘里这个hardware文件夹和system文件夹里都存在ds1302.h,ds1302.c文件,我服了,这样也会出错。......
  • 动态规划·爬楼梯问题(一维)与印章问题(二维)
    算法介绍动态规划(DynamicProgramming)的核心是当前状态的解由上一状态得到。算法将求解问题分解成多个相似的子问题,每个子问题的解由上一个子问题的解得到并储存,往往用于优化递归问题,减少相同子问题的重复计算。一维动态规划·爬楼梯问题问题描述假设你正在爬楼梯,需要n ......