【贪心算法】阿里巴巴与四十大盗——背包问题与0-1背包问题

2023-11-18

前言

关于贪心算法,我在这篇博客中已经做了简单的介绍。初识贪心算法

下面来介绍一下贪心算法中的一个经典的问题——背包问题

一、问题描述

一天,阿里巴巴赶着一头毛驴上山砍柴,无意间在远处发现了一群盗贼,他们把偷窃强盗来的宝物全部藏在一个山洞里,山洞前有一个洞门,只见盗贼首领说了一句“芝麻开门”,顷刻间,洞门大开,里面漏出了阿里巴巴这辈子都没有见过的稀世珍宝。阿里巴巴心想:这些盗贼真的可恨,囤积了这么多盗来的宝物藏匿于此,等他们走掉后,不如偷偷把一部风宝物偷出来分给村里的穷人们,让他们开开眼界,他打算一种宝物只拿一个,如果太重就用锤子锤开分解装走一部分,但是毛驴的运载能力有限,怎样才能用自己的驴子运走最大价值的财宝分给那些穷人们呢?

二、问题分析

简而言之,假设山洞中一共有n种宝物,每一种宝物都有一定的重量w与价值v,毛驴的最大运载能力为m,一种宝物只能拿走一样,且宝物可以分割带走。那么怎样才能使毛驴运走的宝物价值最大呢?

这是一个典型的贪心问题,我们可以尝试一下三种贪心策略:

(1)每次挑选价值最大的宝物装入背包

(2)每次挑选重量最小的宝物装入背包

(3)每次挑选性价比最大的宝物装入背包

我们思考发现:如果选择价值最大的宝物优先装,但是它的重量可能很大;如果选择重量最小的宝物优先装,但是它的价值又不一定大。所以,第一种和第二种贪心策略显然不是好的策略,舍弃。第三章贪心策略从性价比的角度出发,即每一件宝物都有各自的性价比,即:单位价值p=w/v 我们采取性价比最大的宝物优先装载,最终装不下的宝物进行分割,等于最后剩余的装载量乘以该宝物的单位价值,最终合成,那么最终的价值一定是最大的,即最优解。

三、背包问题

代码中有涉及到C++sort排序函数降序排列的方法,不太了解的小伙伴可以去看看我另一篇文章

链接:C++中常用的sort排序函数

背包问题的完整代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int N =1000005;
typedef struct treasure{
	double w;
	double v;
	double p;
}treasure;
//自定义比较函数cmp
bool cmp(treasure t1,treasure t2){
	return t2.p<t1.p;
} 
treasure s[N];
int main(){
	int n,m;
	cout<<"请输入宝物的种类个数n和驴子的最大承载重量m"<<endl;
	cin>>n>>m;
	cout<<"请分别输入这"<<n<<"种宝物所对应的重量与价值"<<endl;
	for(int i=0;i<n;i++){
		cin>>s[i].w>>s[i].v;
		s[i].p=s[i].v/s[i].w;
	} 
	sort(s,s+n,cmp);//对宝物以性价比进行降序排列
	double sum=0.0;
	for(int i=0;i<n;i++){
		if(m>s[i].w){
			m-=s[i].w;
			sum+=s[i].v;
		}
		else{
			sum+=m*s[i].p;//分解宝物,部分装入
			break; 
		}
	} 
	cout<<"能够装入宝物的最大价值为:"<<sum<<"亿元"<<endl;
	return 0;
}

四、0-1背包问题

(1)区别

背包问题与0-1背包问题的区别在哪? 区别就在于剩余物品是否可以进行分割

物品可以进行分割的装载问题称为背包问题,物品不可以分割的装载问题称为0-1背包问题。

(2)原因

在物品不可分割的情况下,即0-1背包问题,其已经不具备贪心选择性质,即原问题的最优解无法通过一系列的局部最优解而得到,因此这类问题最终得到的有可能只是近似解。例如上面的背包问题,如果要求物品不可分割,那么最后剩余的装载空间将永远无法填满,永远闲置,故最终可能无法得到一个最优解,而是最优解的近似解。

(3)代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N =1000005;
typedef struct treasure{
	double w;
	double v;
	double p;
}treasure;
//自定义比较函数cmp
bool cmp(treasure t1,treasure t2){
	return t2.p<t1.p;
} 
treasure s[N];
int main(){
	int n,m;
	cout<<"请输入宝物的种类个数n和驴子的最大承载重量m"<<endl;
	cin>>n>>m;
	cout<<"请分别输入这"<<n<<"种宝物所对应的重量与价值"<<endl;
	for(int i=0;i<n;i++){
		cin>>s[i].w>>s[i].v;
		s[i].p=s[i].v/s[i].w;
	} 
	sort(s,s+n,cmp);//对宝物以性价比进行降序排列
	double sum=0.0;
	for(int i=0;i<n;i++){
		if(m>s[i].w){
			m-=s[i].w;
			sum+=s[i].v;
		}
		else{
			//sum+=m*s[i].p;//分解宝物,部分装入
			break; 
		}
	} 
	cout<<"能够装入宝物的最大价值为:"<<sum<<"亿元"<<endl;
	return 0;
}

 最终价值与背包问题相比少了0.6,这一点就是物品不可分割所造成的。 

最后留一个小问题供大家思考:

为什么在加勒比海盗船——最优装载问题中,船在没有装满还留有剩余空间的情况下,最终所得到的仍然是最优解。而在0-1背包问题中,在没有装满的情况下,最终得到的有可能只是最优解的近似解?

一键三连,动力不竭!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【贪心算法】阿里巴巴与四十大盗——背包问题与0-1背包问题 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 根据属性的类型使用文本框或复选框

    如果我有这样的结构 public class Parent public string Name get set public List
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 对类 static constexpr 结构的未定义引用,g++ 与 clang

    这是我的代码 a cp p struct int2 int x y struct Foo static constexpr int bar1 1 static constexpr int2 bar2 1 2 int foo1 return
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结

随机推荐