问题描述:

给定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. 【问题】vs IIS破除文件上传限制最全版
  2. hdu4829 带权并查集(题目不错)
  3. android 重启app_[Boot]Android系统启动-zygote篇
  4. php保存附件到指定服务器,如何在PHP中将电子邮件附件保存到服务器?
  5. 定时器控件timer winform 114869229
  6. mvc上传图片(上传和预览)webuploader
  7. Tableau Online免费注册试用
  8. 深度测试oppo软件,OPPO深度测试app
  9. linux下使用 sb接口扫描仪,怎样在Linux下设置和使用扫描仪.doc
  10. 增值税普通发票冲红方法
  11. ssh整合错误 0 nanoseconds spent acquiring 0 JDBC connections;
  12. 忍得住清贫 耐得住寂寞 禁得起诱惑
  13. 工欲善其事,必先利其器-程序员工具推荐
  14. 谷歌云端硬盘快速下载方法_如何快速搜索Google云端硬盘
  15. 文件类型识别----魔数
  16. 2023北京理工大学计算机考研信息汇总
  17. 肯塔基大学计算机科学,西肯塔基大学计算机专业
  18. 如何解决KEIL 5 编译KEIL4的带有RTX系统的工程解决方法
  19. gwr模型用什么做_如何对好文章做信息处理,用好文章摘录模型
  20. oracle数字转换成人民币大写

热门文章

  1. Matlab 等间隔读取IGS对流层天顶延迟(ZPD)文件
  2. 全国首个工业大数据交易平台落户杭州
  3. Emoji四字节字符入库时错误的解决方案(Incorrect string value: '\xF0\x9F\x99\x8F' for column 'Reply_Content' at row 1)
  4. 【NLP理论】——文本在计算机中的表示方法总结
  5. 世界首富的22种习惯,值得你…
  6. 极目智能与锐算科技达成战略合作,4D毫米波成像雷达助力智能驾驶落地
  7. Transfer Learning for NLP with TensorFlow Hub
  8. 建筑供配电技术实训装置QY-GDP05
  9. 华为设备中小园区组网
  10. python程序分析_如何分析Python脚本?