动态规划-砝码称重问题
来源:互联网 发布:layout拼图软件 编辑:程序博客网 时间:2024/04/29 02:14
动态规划(Dynamic Programming)这个词乍一听感觉甚是高大上,初次学习或者使用的时候会感觉难以理解,这是正常的,毕竟凡事都是一回生二回熟。其实它也不难的,大家要明白一个道理,能写到课本上给学生学习的东西必然属于不难的东西,因为太难的东西写到课本上读者接受不了,这本书就没有出版的意义了。当然我说的不难也仅仅只是说动态规划的思想不难,因为我们常常面临着一个棘手的问题---那就是这个理论应用到实践中的时候,它没有一个公式或者现成的可以直接用来套用的模型。对于这一点,没有办法,只能是通过多种案例,自己多总结,多思考,看看每一种能用动态规划理论解决的问题,状态模型究竟是如何建立的,而这就需要读者多多练习了。
本文通过华为OJ上一个基本题-砝码称重问题来让初学者消化动态规划。
先来读一读题目,题目如下:
现有一组砝码,重量互不相等,分别为m1、m2……mn;他们可取的最大数量分别为x1、x2……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种情况。
- 动态规划-砝码称重问题
- 砝码称重问题求解---动态规划与母函数方法
- 砝码称重问题求解:动态规划与母函数方法
- 动态规划---砝码称重问题
- 动态规划——砝码称重
- 动态规划——砝码称重
- codevs砝码称重 回溯+动态规划
- 动态规划——砝码称重
- 动态规划练习3 [砝码称重]
- 动态规划——背包系列之砝码称重
- 砝码称重:动态规划与母函数方法
- 砝码称重问题
- 砝码称重问题
- 砝码称重问题
- 砝码称重的问题
- 砝码称重问题
- 砝码称重问题
- 砝码称重问题【dp】
- C#入门经典第6版学习 二
- MarkDown 基本使用方法
- Mysql按降序排序语法和HQL语句的语法
- 演示HashCode
- 简单分析tkprof输出文件的内容
- 动态规划-砝码称重问题
- presto代码解析一
- 欢迎使用CSDN-markdown编辑器
- 演示toString
- 安装apk,启动APP,启动service
- linux 下 ffmpeg 库怎么才可以调试
- 关于导入GPUImage
- Python Decorator 和函数式编程
- 如何实现软键盘不自动弹出