动态规划-砝码称重问题

2023-10-29

    动态规划(Dynamic Programming)这个词乍一听感觉甚是高大上,初次学习或者使用的时候会感觉难以理解,这是正常的,毕竟凡事都是一回生二回熟。其实它也不难的,大家要明白一个道理,能写到课本上给学生学习的东西必然属于不难的东西,因为太难的东西写到课本上读者接受不了,这本书就没有出版的意义了。当然我说的不难也仅仅只是说动态规划的思想不难,因为我们常常面临着一个棘手的问题---那就是这个理论应用到实践中的时候,它没有一个公式或者现成的可以直接用来套用的模型。对于这一点,没有办法,只能是通过多种案例,自己多总结,多思考,看看每一种能用动态规划理论解决的问题,状态模型究竟是如何建立的,而这就需要读者多多练习了。

    本文通过华为OJ上一个基本题-砝码称重问题来让初学者消化动态规划。

    先来读一读题目,题目如下:

    现有一组砝码,重量互不相等,分别为m1、m2……mn;他们可取的最大数量分别为x1x2……xn。现在要用这些砝码去称物体的重量,问能称出多少种不同的重量。

    看完这个题目感觉似乎无从下手,难道要一个个的枚举拼凑吗?答案是否定的。不绕弯子了,下面直接进入动态规划的主场。我们假定砝码的重量单位是克,下文中不添加这个单位,只说数字,比如重量是1,表示重量是1克。

    设想第0种情况,假如现在只有0个砝码,问它能称出的重量有几种?是个人都知道只能称0的重量,并且只有这么1种情况。

    设想第1种情况,现在只有1种规格的砝码,它的重量是1,数量是1个,问它能称出的重量有几种?很明显,能称出0和1这2种重量,一共2种情况。

    设想第2种情况,现在有2种规格的砝码,它们的重量分别是1和2,1克的砝码1个,2克的砝码1个,问它能称出的重量有几种?用手比划比划应该能知道,能称出0,1,2,3这4种重量,4种情况。

    。。。

    。。。

    设想第n种情况,现在有n种规格的砝码,他们的重量分别为m1,m2。。。mn,问它能称出的重量是几种?这个时候貌似不是用手比划比划就可以出来结果了。但是这个问题的提出,就相当于是题目最终要解决的问题了。如果能够利用某种规律回答这第n种情况的提问,那么砝码称重的问题就可以完全解决。问题来了,上面的分析过程是否存在一个规律呢?答案是规律肯定存在,并且分析这种规律的思路就是动态规划的思路。

    现在我们把第n种情况能称出的重量种数用f(n)来表示,可以试着这样具体化的描述,第0种情况能称出的重量有f(0)种,第1种情况能称出的重量有f(1)种,第2种情况能称出的重量有f(2)种,。。。第n种情况能称出的重量有f(n)种。

    下面,回归到上面的提到的情况分析中去。

    第0种情况,f(0)=1;

    第1种情况,f(1)=2;如果把这个结果和第0种情况结合起来看,可以认为:f(1)=f(0)+1,此式意义非凡,它的意义为:第1种情况可以建立在第0种情况的基础上去解决,单单来看情况1的砝码可以称1种重量(即用一个1克的砝码称1克的物体)不同于情况0中,二者相加即为f(1)的结果。

    第2种情况,f(2)=4=f(1)+2,为什么加2?因为如果仅仅只有1个1克的砝码和1个2克的砝码,它只能称出重量为2克,3克这两种不同于情况1中的重量(情况1中已经解决了0克和1克的问题)。

    现在假定已经解决了第n-1种情况,即f(n-1)的值已经知道,那么我们如果能计算出单单第n种情况能称出哪些不同于n-1种情况中的重量,假定为x种,那么f(n)=f(n-1)+x。最终问题便得到了解决。

    请仔细领会上面的这个分析过程,一定要弄明白。

    下面附上C++的程序,本程序先计算第0种砝码能称出多少种重量,能称出的重量均在flag[100000]数组中做好标记,然后计算第0种砝码最多能称多大的重量,在这个基础上加入第1种砝码,然后计算第2种,直至第n种。程序最关键的地方在于29行采用CurrentWeight逐次加1试探的方式得到一个数值,然后在33行验证这个CurrentWeight是否是前一种情况中的砝码可以称出来的重量,如果是,表明新的重量值确实可以称出来,那么就把这个重量在flag[100000]数组中标记。最后统计标志数组flag[100000]种1的次数,即为所有砝码随意组合可以称出的重量。

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int fama(int N, int weight[], int num[])
{
	int AllWeight = 0, i, j;
	for (i = 0; i < N; i++)
		AllWeight = AllWeight + weight[i] * num[i];
	int flag[100000] = {0};

	/*先计算第0种砝码能够得到的称重数量,并且计算第0种砝码最大的重量*/
	int TempWeight = 0;
	for (i = 0; i <=num[0]; i++)
	{
		flag[weight[0] * i] = 1;
	}
	TempWeight = weight[0]*num[0];
	//从此以后TempWeight将用来表示前一种砝码的最大重量
	
	i = 1;//从第1种砝码开始做
	int CurrentWeight;
	int NewWeight;
	while (i < N)
	{
		for (j = 1; j <=num[i]; j++)//第i种砝码的个数最多为num[i]
		{
			for (CurrentWeight = 0; CurrentWeight <=TempWeight; CurrentWeight++)//CurrentWeight采用试探的方式逐次加1它的大小不能大于前一种重量的最大值
			{
				NewWeight = CurrentWeight + j*weight[i];
				if (NewWeight>AllWeight) break;
				if (flag[CurrentWeight==1]&&flag[NewWeight]==0)//如果当前的这个重量可以由前一种砝码和当前砝码组合而成
				{
					flag[NewWeight] = 1;
				}
			}
		}
		TempWeight = TempWeight + num[i] * weight[i];//更新上一个砝码的最大重量
		i++;
	}

	/*统计总数量*/
	int count = 0;
	for (i = 0; i < 1000; i++) { if (flag[i] == 1) count++; }
	return count;
}

int main()
{
	int n;
	cin >> n;
	int *w = new int[n];
	int *num = new int[n];
	for (int i = 0; i < n; i++)
		cin >> w[i];
	for (int i = 0; i < n; i++)
		cin >> num[i];

	cout <<fama(n,w,num)<<endl;
	return 0;
}

    总结动态规划的思路就是--总是试图从最简单的情况开始着手找出答案,利用最简单的情况的答案计算下一种情况的答案。存在一种规律使得f(n)=f(n-1)+x的模式,使我们可以利用循环体计算n种情况。


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

动态规划-砝码称重问题 的相关文章

  • boost::asio + std::future - 关闭套接字后访问冲突

    我正在编写一个简单的 TCP 客户端来发送和接收单行文本 异步操作由 std future 处理 以便于超时阻塞查询 不幸的是 我的测试应用程序在破坏服务器对象时因访问冲突而崩溃 这是我的代码 TCP客户端 hpp ifndef TCPCL
  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 启动时出现 OData v4 错误:找不到段“Whatever”的资源

    我正在构建新的 v4 服务 一切进展顺利 直到我为新模型 实体添加了新控制器 并在启动站点进行测试运行时收到此错误 控制器似乎编码正确 就像其他控制器一样 控制器 CustomersOData 中的操作 GetFeed 上的路径模板 Cus
  • 如何在 C# 中从 UNIX 纪元时间转换并考虑夏令时?

    我有一个从 unix 纪元时间转换为 NET DateTime 值的函数 public static DateTime FromUnixEpochTime double unixTime DateTime d new DateTime 19
  • 如何为 C 分配的 numpy 数组注册析构函数?

    我想在 C C 中为 numpy 数组分配数字 并将它们作为 numpy 数组传递给 python 我可以做的PyArray SimpleNewFromData http docs scipy org doc numpy reference
  • 如何将 #ifdef DEBUG 添加到 Xcode?

    我的项目中有一些代码永远不应该在发布版本中使用 但在测试时很有用 我想做这样的事情 ifdef DEBUG Run my debugging only code endif 在 Xcode 4 中哪里添加 DEBUG 设置 我尝试将其放入
  • 在 Unity 进程和另一个 C# 进程之间进行本地 IPC 的最快方法 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我希望每秒大约 30 次从 C 应用程序向我的 Unity 应用程序传送大量数据 由于 Unity 不支持映射内存和管道 我考虑了 t
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 生成(非常)大的非重复整数序列而不进行预洗牌

    背景 我编写了一个简单的媒体客户端 服务器 我想生成一个不明显的时间值 随从客户端到服务器的每个命令一起发送 时间戳中将包含相当多的数据 纳秒分辨率 即使它不是真正准确 因为现代操作系统中计时器采样的限制 等 我想做的 在 Linux 上
  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • Visual Studio 中的测试单独成功,但一组失败

    当我在 Visual Studio 中单独运行测试时 它们都顺利通过 然而 当我同时运行所有这些时 有些通过 有些失败 我尝试在每个测试方法之间暂停 1 秒 但没有成功 有任何想法吗 在此先感谢您的帮助 你们可能有一些共享数据 检查正在使用
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • 有人可以提供一个使用 Amazon Web Services 的 itemsearch 的 C# 示例吗

    我正在尝试使用 Amazon Web Services 查询艺术家和标题信息并接收回专辑封面 使用 C 我找不到任何与此接近的示例 所有在线示例都已过时 并且不适用于 AWS 的较新版本 有一个开源项目CodePlex http www c
  • gcc 的配置选项如何确定默认枚举大小(短或非短)?

    我尝试了一些 gcc 编译器来查看默认枚举大小是否很短 至少一个字节 强制使用 fshort enums 或无短 至少 4 个字节 强制使用 fno short enums user host echo Static assert 4 si
  • C++ 密码屏蔽

    我正在编写一个代码来接收密码输入 下面是我的代码 程序运行良好 但问题是除了数字和字母字符之外的其他键也被读取 例如删除 插入等 我知道如何避免它吗 特q string pw char c while c 13 Loop until Ent
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的

随机推荐

  • 关于Ionic2\Angular2使用http的一些坑

    1 服务器接收key value key value类型的值 但服务器无法获取到Post请求的body的值 描述 使用url key value key value的形式可以正常请求到参数 但是把参数放入到body后 服务器估计获取到信息但
  • Kotlin项目类找不到bug:java.lang.ClassNotFoundException: kotlin.reflect.Kotlin Reflect Internal Error

    一 今天在创建了一个Kotlin Spring的项目 结果启动报错 org springframework context ApplicationContextException Unable to start web server nes
  • Python - selenium自动化-Chrome(wap模式)

    Selenium Chrome浏览器如何模拟手机操作 进入手机模式 打开谷歌浏览器 按F12 进入开发者模式 点击Toggle device toolbar 进入手机模式 设置Chrome的手机模式 deviceName可更改成Chrome
  • 人工智能 机器学习实验总结

    答案仅供参考 1 数据预处理 给定数据集datingTest 实验任务 读取DatingTest的数据文件 1 并输出第一列数据的最大 最小和均值 2 输出该文件有多少数据 3 计算第一条数据和第二条数据的欧式距离 import panda
  • 面试题 v-if跟v-show的区别

    v if v show 区别 展示形式不同 v if是 创建一个dom节点 V show 是 display none block 使用场景不同 初次加载 v if 要 比v show 好 页面不会做加载盒子 频繁切换 v show 要比
  • JDK8新特性(七)之Stream流的count()、filter()、limit()、skip()方法

    1 Stream流的count 方法 Stream流提供count方法来统计其中的元素格式 long count 该方法返回一个long值代表元素个数 基本使用 import java util ArrayList import java
  • cmake

    cmake的常用命令 cmake minimum required message project set add executable add compile options add subdirectory add library ta
  • python -- 替换netcdf文件中的时间

    最近 在处理nc文件时 在时间上存在部分缺失的数据 为了避免影响后续操作 这里通过复制前一时刻的nc数据进行替代 但是虽然缺失时刻的数据得到了填充 但是填充的数据的时间属性本质上仍然是前一时刻的 为了保证时间的一致性 这里通过一个更新时间的
  • ARM(IMX6U)裸机C语言蜂鸣器驱动实验(BSP+SDK)

    参考 Linux之ARM IMX6U 裸机C语言蜂鸣器驱动实验 驱动编写 编译 作者 一只青木呀 发布时间 2020 08 16 14 47 23 网址 https blog csdn net weixin 45309916 article
  • android adb常用指令

    Android 调试桥 adb 是多种用途的工具 该工具可以帮助你你管理设备或模拟器 的状态 可以通过下列几种方法加入adb 在设备上运行shell命令 通过端口转发来管理模拟器或设备 从模拟器或设备上拷贝来或拷贝走文件 下面对adb进行了
  • C++数组的正确释放方式

    include
  • mariadb日志报错:error while loading shared libraries: libjemalloc.so.2处理办法

    Linux下找不到so文件的解决办法 rznice 2016 03 11 16 39 27 19494 收藏 1 分类专栏 linux 版权 最近在安装完tengine 在启动tengine时报找不到libjemalloc so 2的提示
  • # Odoo丨Odoo框架源码研读一:前后端交互

    Odoo丨Odoo框架源码研读一 前后端交互 本期内容 Odoo框架源码研读之 前后端交互 Odoo框架是一款企业应用快速开发平台 适用于各种规模的企业应用 安装与使用十分广泛 Odoo框架源码的第一篇研读内容 前后端交互 源码文件结构 O
  • 浅析Python中“if __name__ == __main__”的意义

    首先可以用一句话概括 if name main 语句的意义是为了使当前脚本可以正常执行 在被其他脚本调用时也可以执行 举个栗子 print py 文件中的代码如下 print the first if name main print the
  • uniapp 子组件向父组件传值

    使用组件可以减少代码的重复率 提高写代码的效率 改起来也方便 最近在使用uni app做项目 一套代码多端实现 做些简单的项目还是可以的 废话少说 说说子组件向父组件传值 子组件获取到值的时候 使用 emit传给父组件 this emit
  • c++函数为什么带imp_二次函数含参最值问题,老师怎么讲学生都不明白,试试这九张动图...

    一入函数深似海 从此数学是路人 很多同学都有这样的感觉 问 数学是从什么开始听不懂了 答 学函数的时候 函数问题作为中学阶段数学重要的知识点 真的是难倒了很多同学 数学老师也非常的痛苦 每次讲完函数问题 看到大家一脸茫然的表情 都会狠狠心再
  • Mac M1 Xcode创建动态链接库dylib(c++)——JNA-JNI(三)

    Mac M1 Xcode创建动态链接库dylib c JNA JNI 三 系列文章 Java通过JNI调用C 动态链接库dll 并打在jar包内 JNA JNI 一 Java使用JNA调用C 动态链接库 JNA JNI 二 Mac M1 X
  • 学1个月python爬虫就月赚6000?告诉你爬虫的真实情况!

    用爬虫赚外快的事情我也干了很多年 爬虫自然不在话下 那么今天我来说说5个深入一点的爬虫问题 让你清楚爬虫的真实情况 1 现在的爬虫接单真能1个月赚6000的快外 2 初级爬虫只能接一些小单 怎样才算初级爬虫水平 3 中级爬虫是职业爬虫工程师
  • 自学c++笔记(三)

    笔记记录本人学习C 路上的一些摘要与总结 供本人阅读同时也分享与他人 转义序列 换行符 n 水平制表符 t 退格 b 回车 r 振铃 a wchar t 宽字符类型 是一种整型类型 使用wcin和wcout来处理wchar t流 const
  • 动态规划-砝码称重问题

    动态规划 Dynamic Programming 这个词乍一听感觉甚是高大上 初次学习或者使用的时候会感觉难以理解 这是正常的 毕竟凡事都是一回生二回熟 其实它也不难的 大家要明白一个道理 能写到课本上给学生学习的东西必然属于不难的东西 因