• 题解——01背包问题例题(装箱问题、宠物小精灵之收服)


    01背包问题:

    给定一个有一定容量的背包,和 n 个物品,每个物品只能选择一次;

    每个物品有其对应的体积和价值;

    问背包最多能装下的物品的最大价值为多少。

    输入格式:

    第一行两个整数,N,V,分别表示物品数量和背包容积;

    接下来有 N 行,每行两个整数 vi,wi 用空格隔开,分别表示第 i 件物品的体积和价值。

    输出格式:

    输出一个整数,表示最大价值。
     

    思路:

    假设f[i][j]代表当只选择前n个物品并且背包容量只有j的时候所能装下物品的最大价值

    f[i][j] = max(f[i - 1][j],f[i - 1][j - v[j]] + w[j])

    题目的答案就是 f[n][m] 。

    为什么这样做是正确的呢:

    以集合的角度:

    当我们在面对第i件物品的时候,我们一定会在选择第i件物品和不选择第i件物品这两种情况中做出选择;

    假设我们有一个集合,集合表示的是只看前i件物品,背包容量为j的时候的所有物品选法,f[i][j] 取集合中的最大值,该集合可以划分为选择第i件物品和不选择第i件物品两个集合,f[i][j] 取两个集合中的最大值即可。

    图示:

     代码模板如下:

    1. #include<iostream>
    2. using namespace std;
    3. const int N = 1010;
    4. int v[N], w[N];//每个物品的体积和价值
    5. int f[N][N];//每个状态的最大价值
    6. int n, V;//物品数和背包容量
    7. int main() {
    8. cin >> n >> V;
    9. for (int i = 1; i <= n; i++)
    10. cin >> v[i] >> w[i];
    11. for (int i = 1; i <= n; i++)
    12. for (int j = 0; j <= V; j++) {
    13. f[i][j] = f[i - 1][j];
    14. if (j >= v[i])f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
    15. }
    16. cout << f[n][V];
    17. return 0;
    18. }

    优化至一维:

    我们发现,我们在更新的时候只会用到两层状态,每一个状态都是由上一层左边的状态推导而来的,我们不妨将这两行合并。

    dp方程为:

    f[j] = max(f[j], f[j - v] + w)

    一维状态中,f[j] 默认就是上一层的 f[j],所以不用进行不选第 i 个物品的更新;

    当选择第 i 个物品时,需要用到上一层左边的状态,所以不能把本层左边的状态更新成上一层左边的状态;

    如果从前往后进行更新,那么上一层的左边的状态就被更新掉了,故应该从后往前更新。

    图示:

     代码模板如下:

    1. #include
    2. using namespace std;
    3. const int N = 1010;
    4. int n, V;//物品数
    5. int v, w;//总体积
    6. int f[N];//状态
    7. int main() {
    8. cin >> n >> V;
    9. for (int i = 0; i < n; i++) {
    10. cin >> v >> w;
    11. for (int j = V; j >= v; j--)
    12. if (v <= j)f[j] = max(f[j - v] + w, f[j]);//f[j]默认就是上一行的值,所以不用更新v小于j的情况
    13. }
    14. cout << f[V];
    15. return 0;
    16. }

    例题1 . 装箱问题

    [NOIP2001]装箱问题 - C语言网 (dotcpp.com)

    题目要尽可能的装满箱子;

    给每个物品赋予一个价值属性,价值的值就是体积的值;

    即求一定体积下的最大价值是多少。

    AC代码如下:

    1. //二维
    2. #include
    3. using namespace std;
    4. const int N = 20010, M = 40;
    5. int f[M][N];
    6. int n, m;//物品数和背包容量
    7. int main() {
    8. cin >> m >> n;
    9. int v, w;
    10. for (int i = 1; i <= n; i++) {//前i个物品
    11. //读入体积和价值
    12. cin >> v;
    13. w = v;
    14. for (int j = 0; j <= m; j++) {//体积为j
    15. f[i][j] = f[i - 1][j];//不选该物品
    16. if (j >= v)f[i][j] = max(f[i][j], f[i - 1][j - v] + w);
    17. }
    18. }
    19. cout << m - f[n][m];//记得做减法
    20. return 0;
    21. }
    22. //一维
    23. #include
    24. using namespace std;
    25. const int N = 20010;
    26. int f[N];
    27. int n, m;//物品数和背包容量
    28. int main() {
    29. cin >> m >> n;
    30. int v, w;
    31. for (int i = 1; i <= n; i++) {//前i个物品
    32. //读入体积和价值
    33. cin >> v;
    34. w = v;
    35. for (int j = m; j >= v; j--) {//从后往前更新
    36. f[j] = max(f[j], f[j - v] + w);
    37. }
    38. }
    39. cout << m - f[m];//记得做减法
    40. return 0;
    41. }

    例题2 . 数字组合:

    信息学奥赛一本通T1291-数字组合 - C语言网 (dotcpp.com)

    f[i][j] 表示只看前 i 个物品,和为 j 的情况;

    如果选择第 i 个物品:f[i][j] = f[i-1][j-v[i]];

    如果不选第 i 个物品:f[i][j] = f[i-1][j];

    f[i][j]等于两者之和。

    该题需要初始化,f[0][0] = 1。

    AC代码如下:

    1. //二维
    2. #include
    3. using namespace std;
    4. const int N = 110, M = 10010;
    5. int f[N][M];
    6. int n, m;//n个数和为m
    7. int main() {
    8. cin >> n >> m;
    9. int v;
    10. f[0][0] = 1;
    11. for (int i = 1; i <= n; i++) {//前i个数
    12. cin >> v;
    13. for (int j = 0; j <= m; j++) {
    14. f[i][j] += f[i - 1][j];//不选
    15. if (j >= v)f[i][j] += f[i - 1][j - v];//选
    16. }
    17. }
    18. cout << f[n][m];
    19. return 0;
    20. }
    21. //一维
    22. #include
    23. using namespace std;
    24. const int M = 10010;
    25. int f[M];
    26. int n, m;//n个数和为m
    27. int main() {
    28. cin >> n >> m;
    29. int v;
    30. f[0] = 1;//初始化
    31. for (int i = 1; i <= n; i++) {//前i个数
    32. cin >> v;
    33. for (int j = m; j >= v; j--)
    34. f[j] += f[j - v];//选
    35. }
    36. cout << f[m];
    37. return 0;
    38. }


     

  • 相关阅读:
    第六章 队列的讲解与实现
    Linu性能调优:面对DDOS攻击,我们如何缓解局面?
    深入了解苹果证书及其分类,提升iOS应用开发效率
    基于PHP+MYSQL宠物领养系统的开发与设计
    unity URP 利用particle system制作简单的shader交互
    6.运行mysql容器-理解容器数据卷
    Trinitycore学习之在Linux环境上搭建服务器并测试运行
    MATLAB实现TopSis优劣解距离法——分析《世界征服者3》将领排名
    什么是IOC和什么是AOP
    python调用接口脚本
  • 原文地址:https://blog.csdn.net/weixin_56265979/article/details/128045653