问题描述:

给定n种物品和一背包。物品i的重量为wi,其价值为vi, 背包容量为c。问应如何选择装入背包中的物品,使得背入背包的物品的总价值最大?

解析:

此问题形式化的描述是,给定c > 0, wi, vi, 1 <= i <= n(c为背包容量), 要找出一个n元0-1向量(x1, x2, ... , xn),xi ∈ {0, 1}, 1 <= i <= n, 使得∑ wixi (i 从 1 到 n 求和)<= c ,而且∑ wixi (i 从 1 到 n 求和)达到最大。因此0-1背包问题是一个特殊的整数规划问题。

方法1:

递归关系:(这里与课本的描述不同个人感觉课本的“加”比较难理解, 这里用的“减”, 相信我继续看下去QAQ, 方法2用课本的方法“加”)

设所给0-1背包问题的子问题的最优值为m[i][j], 即m[i][j]的含义是是在背包容量为j,可选物品为1, 2, 3, ..., i 时的0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可建立计算m[i][j]的递归式如下:

m[i][j] = max{m[i - 1][j], m[i - 1][j - w[i]] + v[i]}     j >= w[i];

= m[i - 1][j]                                                    j < w[i];

递归关系流程化:

1:如果不放入第 i 件物品,则问题转换为“前i - 1件物品放入容量为 j 的背包中”;

2:如果放入第 i 件物品,则问题转化为“前i - 1件物品放入容量为 j - w[i] 的背包中”,此时的最大值为 max{m[i - 1][j], m[i - 1][j - w[i]] + v[i] }.

代码:

/*
输入样例 1:3 10
4 3
5 4
6 5
输出为:最优解为 : 11
选择的物品的序号为 :
2 3输入样例 2:5 10
6 2
3 2
5 6
4 5
6 4
输出为:最优解为 : 15
选择的物品的序号为 :
1 2 5
*/
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1024;
int n; //物品个数
int c; //包的容量
int value[MAX]; //物品的价值
int weight[MAX]; //物品的重量
int x[MAX]; //n元0-1向量
int m[MAX][MAX]; //解的容器
void Input()
{scanf("%d %d", &n, &c);for(int i = 1; i <= n; ++i)scanf("%d %d", &value[i], &weight[i]);
}
//创建最优解
void Knapsack()
{memset(m, 0, sizeof(m));for(int i = 1; i <= n; ++i) //逐行填表,i表示当前可选物品数,j表示当前背包的容量, 也就是从低到顶。 {for(int j = 1; j <= c; ++j){if(j < weight[i])m[i][j] = m[i - 1][j];else{m[i][j] = max(m[i - 1][j], m[i - 1][j - weight[i]] + value[i]);}}}
}
//获取最优解(即设法将求得的最优解输出出来)
void Traceback()
{int cc = c;for(int i = n; i > 1; --i){if(m[i][cc] == m[i - 1][cc])x[i] = 0;else{x[i] = 1;cc -= weight[i];}}if(cc >= weight[1])x[1] = 1;}
void Output()
{cout << "最优解为 : " << m[n][c] << endl;cout << "选择的物品的序号为 :" << endl;for(int i = 1; i <= n; ++i)if(x[i] == 1)cout << i << " ";cout << endl;
}
int main()
{Input();Knapsack();Traceback();Output();
}

参考博客 :https://www.cnblogs.com/vincently/p/4804225.html, 补充了Traceback()求解的过程, 完善了代码。

方法2:(解释见代码注释)

递归关系:

设所给0-1背包问题的子问题的最优值为m[i][j], 即m[i][j]的含义是背包容量为j,可选物品为i, i + 1, ... n 时的0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可建立计算m[i][j]的递归式如下:

m[i][j] = max{m[i + 1][j], m[i + 1][j - w[i]] + v[i]}     j >= w[i];

= m[i + 1][j]                                                    j < w[i];

递归关系流程化:

1:如果不放入第 i 件物品,则问题转换为“i + 1, i + 2,  ..... , n件物品放入容量为 j 的背包中”;

2:如果放入第 i 件物品,则问题转化为“i + 1, i + 2,  ..... , n 件物品放入容量为 j - w[i] 的背包中”,此时的最大值为 max{m[i + 1][j],  m[i + 1][j - w[i]] + v[i]}.

与方法1的区别:

请先务必先看懂方法1~。

首先我们知道求最优解的过程就是填表m的过程。

方法1是从第一行开始填表m,而方法2是从第n行开始填表m即(两种方法都是是从底往上求解,不过方法一是可选物品从1-> i(从左到右),方法二是可选物品从i -> n(从右向左)。这就是区别。而思路完全一样。请务必好好想想,然后就会恍然大悟٩(๑>◡<๑)۶

代码:

总的来说方法2比方法1运行速度快。

/*
输入样例 1:3 10
4 3
5 4
6 5
输出为:最优解为 : 11
选择的物品的序号为 :
2 3输入样例 2:5 10
6 2
3 2
5 6
4 5
6 4
输出为:最优解为 : 15
选择的物品的序号为 :
1 2 5
*/
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1024;
int n; //物品个数
int c; //包的容量
int value[MAX]; //物品的价值
int weight[MAX]; //物品的重量
int x[MAX]; //n元0-1向量
int m[MAX][MAX]; //解的容器
void Input()
{scanf("%d %d", &n, &c);for(int i = 1; i <= n; ++i)scanf("%d %d", &value[i], &weight[i]);
}
//创建最优解
void Knapsack()
{int jMax = min(c, weight[n] - 1);//这一块填最后m表的最后一行/*解释一下就是:“在可选的物品只有n即最后一个物品n,包的容量为c时” 的最优解。第一个for循环:容易知道在背包容量为0 ~ weight[n] - 1的时候背包是放不进去物品n的,如果背包的容量小于物品n的质量,背包也是放不进去物品的,所以从weight[n] - 1 和 c 中选择一个较小的,然后m[n][0:jMax]的值为零第二个for循环:自然可知当背包容量大于weigh[n]的时候,由于其可选则的物品只有 物品n,因此m[n][weight[n]:c]的值全部为value[n].*/for(int j = 0; j <= jMax; ++j)m[n][j] = 0;for(int j = weight[n]; j <= c; ++j)m[n][j] = value[n];//这一块是填m表的2 ~ n - 1行,容易理解for(int i = n - 1; i > 1; --i){jMax = min(c, weight[i] - 1);for(int j = 0; j <= jMax; ++j)m[i][j] = m[i + 1][j];for(int j = weight[i]; j <= c; ++j)m[i][j] = max(m[i + 1][j], m[i + 1][j - weight[i]] + value[i]);}//这里是填m表的第一行,好好理解一下,不难,好好考虑一下 φ(>ω<*)m[1][c] = m[2][c];if(c >= weight[1])m[1][c] = max(m[1][c], m[2][c - weight[1]] + value[1]);
}
//获取最优解(即设法将求得的最优解输出出来)
void Traceback()
{int cc = c;for(int i = 1; i < n; i++)if(m[i][cc] == m[i + 1][cc])x[i] = 0;else{x[i] = 1;cc -= weight[i];}x[n] = (m[n][cc]) ? 1 : 0;
}
void Output()
{cout << "最优解为 : " << m[1][c] << endl;cout << "选择的物品的序号为 :" << endl;for(int i = 1; i <= n; ++i)if(x[i] == 1)cout << i << " ";cout << endl;
}
int main()
{Input();Knapsack();Traceback();Output();
//    cout << "*******" << endl;
//    for(int i = 1; i <= n; ++i)
//    {
//        for(int j = 1; j <= c; ++j)
//            cout << m[i][j] << " ";
//        cout << endl;
//    }
}

方法三:基于以方法一的路径压缩

思路:

定义m[j] : m[j]为背包容量为 j 时(注意不是剩余容量),背包装入的最大value。

解题流程:遍历 i (可选物品为 1 ~ i),m[j] = max{ m[j], m[j - weight[i]] + value[i] }

缺点:无法求出选中了哪些物品

#include <bits/stdc++.h>
using namespace std;
const int MAX = 1024;
int n; //物品个数
int c; //包的容量
int value[MAX]; //物品的价值
int weight[MAX]; //物品的重量
int x[MAX]; //n元0-1向量
int m[MAX]; //解的容器
void Input()
{scanf("%d %d", &n, &c);for(int i = 1; i <= n; ++i)scanf("%d %d", &value[i], &weight[i]);
}
//创建最优解
void Knapsack()
{memset(m, 0, sizeof(m));for(int i = 1; i <= n; ++i){for(int j = c; j >= weight[i]; --j){m[j] = max(m[j], m[j - weight[i]] + value[i]);}}
}
void Output()
{cout << "最优解为 : " << m[c] << endl;cout << endl;
}
int main()
{Input();Knapsack();Output();
}

0-1背包问题详解-动态规划-两种方法相关推荐

  1. 模糊匹配 读音_onenote搜索机制详解②:两种搜索模式,模糊与精确匹配

    先从纯文本搜索讲起,这是最基本也是最重要的. 从这篇开始,以及接下来连续几篇文章,都会介绍搜索的基础功能.注意,这几篇文章中谈论的都是基本的.正常的搜索功能,暂时不考虑Bug等因素. 在很多软件(例如 ...

  2. python获取屏幕文字_详解:四种方法教你对Python获取屏幕截图(PyQt , pyautogui)...

    前言: 今天为大家带来的内容是详解:四种方法教你对Python获取屏幕截图(PyQt , pyautogui)本文具有不错的参考意义,希望能够帮助到大家! Python获取电脑截图有多种方式,具体如下 ...

  3. 5 获取窗口位置_详解:四种方法教你对Python获取屏幕截图(PyQt , pyautogui)

    前言: 今天为大家带来的内容是详解:四种方法教你对Python获取屏幕截图(PyQt , pyautogui)本文具有不错的参考意义,希望能够帮助到大家! Python获取电脑截图有多种方式,具体如下 ...

  4. 背包问题详解-动态规划

    目录 01背包问题 暴力法 空间优化 背包问题的所有情况 完全背包问题 状态转移方程分析 空间优化 方法二:记录第i件物品的数据k 方法三:转换成01背包 多重背包 方法一:记录第i件物品的数据k 方 ...

  5. c语言怎么改变程序的图标,VC6.0 控制台程序添加图标的两种方法

    如何给C控制台程序添加图标说来很惭愧的问题,C语言也算学了很长一阵子,目前还是停留在控制台的水平,今天用着用着突然想给程序换个图标,却找不到在哪设置,又没窗体,在哪弄呢?百度N久,找到如下两种解决方案 ...

  6. java实现网页保存_详解Java两种方式简单实现:爬取网页并且保存

    对于网络,我一直处于好奇的态度.以前一直想着写个爬虫,但是一拖再拖,懒得实现,感觉这是一个很麻烦的事情,出现个小错误,就要调试很多时间,太浪费时间. 后来一想,既然早早给自己下了保证,就先实现它吧,从 ...

  7. C语言的注释形式及作用,C语言注释详解(两种注释方式)

    在编写C语言源代码时,应该多使用注释,这样有助于对代码的理解.在C语言中有两种注释方式: 一种是以/*开始.以*/结束的块注释(block comment): 另一种是以//开始.以换行符结束的单行注 ...

  8. FTP文件传输协议原理详解(两种工作模式)

    初始FTP     文件传输协议(File Transfer Protocol,缩写:FTP)是一个用于在计算机网络上在客户端和服务器之间进行文件传输的应用层协议.文件传送(file transfer ...

  9. 重根迭代法解方程(两种方法)(Python实现)

    简述 通过两种不同的重根迭代的来解方程. 处理的方程是 (sin(x) - x/2) ^2 = 0 代码 采用的第一种迭代重根迭代方法: xk+1=xk−mf(xk)f′(xk)xk+1=xk−mf( ...

最新文章

  1. 深入解析和反思携程宕机事件
  2. Flask 数据迁移 报错 Table 'xxx' is already defined for this MetaData instance
  3. php和python对比-从PHP与Python的语言比较去了解什么是图灵完备
  4. 善于 调用Windows API
  5. 【集合论】卡氏积 ( 卡氏积概念 | 卡氏积示例 | 卡氏积性质 | 非交换性 | 非结合性 | 分配律 | 有序对为空 | n 维卡氏积 | n 维卡氏积个数 | n维卡氏积性质 )
  6. 2017/5 JavaScript基础4--- 表达式、运算符
  7. 【Python刷题】_6
  8. xMedia来了!支付宝客户端的智能化“武器”
  9. linux w 命令参数解释
  10. c 编程语言概述,C编程语言概述
  11. vue-router 路由嵌套显示不出来_45. Vue路由vuerouter的基本使用
  12. 8 一点就消失_消失的莉莉安(25)
  13. 混沌思维模型实战课:如何发现击穿破局点的单一要素?
  14. 利用navicat 进行 mysql建表语句转oracle建表语句
  15. solidworks有限元分析_新手学习心得体会
  16. 数据分析,怎么做才够“精准”
  17. 面试技巧-面试官的考题
  18. xubuntu language support
  19. VMware安装树莓派(一)
  20. 测试参持之以恒兵勋章活动

热门文章

  1. 文科计算机大专学校有哪些专业,文科的大专学校有哪些?附2020年文科大专学校排名及分数线...
  2. 程序猿常用的Mac编程软件下载
  3. 计量经济学及Stata应用 第二章 Stata入门
  4. 微信、QQ自动抢红包外挂(绿色、无广告--附源码)
  5. vue前端生成二维码并提供二维码下载
  6. 史上最全的Web安全相关网址汇总
  7. 肠道核心菌属——双歧杆菌属,了解并拥有它
  8. 【玩转CSS】渐变色
  9. 部署traefik2.2
  10. 【计算机毕业设计】java ssm房屋租赁系统