01背包问题:初入动规坑
2016-04-11 22:42
423 查看
**
枚举的规律很简单,只需要分三种情况:
①:已经没有剩余的物品了,直接return 0;
②:如果挑选了这个物品重量就会超出限制,跳过这个物品;
③:如果可以挑选这个物品,只需要把挑选和不挑选都尝试一下就好了。
代码如下:
但是这种方法的搜索深度是n,而且每层的搜索有需要两次分支,最坏情况下就需要O(2^n)的时间复杂度,当n比较大时就不具有可执行性了。怎么办才好呢?当我们分析程序的执行过程时,我们发现有很多值出现了多次,由于栈与栈的相互独立原理,每次计算都是与其他计算无关的。所以我们可以把第一次计算的结果给记录下来,省略掉第二次以后的重复计算试试看。
那么就模拟一个二维数组,由于一共n个物品,可以带走的总重量不超过W,所以就创建一个n*W的数组,并通过宏定义将其模拟为二维数组,将每个数第一次的计算结果记录下来。在函数调用时,如果发现结果已经计算过的话就直接使用之前计算的结果。原理已经知道了,那就来试一下吧:
这样就通过记忆优化和递归的方式实现了对问题的求解。
这样微小的改动到底带来了多少速度呢?对于同样的参数,只会在第一次调用时执行递归部分,第二次调用时就可以直接在数组中找到对应的值。参数的组合不超过n*W种,所以只需要O(n*W)的时间就能解决这个问题。
当然,我们可以用非递归替换递归,这样子就不需要入栈出栈操作,程序的运行速度就可以进一步提高。非递归与递归没有太大差别,所以第二个程序写出来后,就可以直接站撸非递归算法了,写法很简单,在数组里循环求解就行了。
源代码可以直接编译运行,但是建议大家不要直接拷贝下去运行。其实你我心里也都明白,算法是思想,而不是单纯的代码,希望你们都能认真分析原理,总结题目的经验和特点,在下次遇到类似题目的时候不必上网查。
我是算法吹,以后会给大家带来更多精彩的算法。
01背包问题
**有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
这是被称为背包问题的一个著名问题。这个问题怎么求解好呢?这个问题是我遇到的第一个动规问题,最开始做的时候并不知道动规的思想,所以直接使用了最简单的方法:对每个物品是否放入背包进行枚举,然后对所有情况求最大值。枚举的规律很简单,只需要分三种情况:
①:已经没有剩余的物品了,直接return 0;
②:如果挑选了这个物品重量就会超出限制,跳过这个物品;
③:如果可以挑选这个物品,只需要把挑选和不挑选都尝试一下就好了。
代码如下:
#include <stdio.h> #include <stdlib.h> static int rec(int, int, int *, int *, int); static int max(int, int); int main() { int n, W; printf("Please input n & W : \n"); scanf("%d %d",&n,&W); int * w = (int *)malloc(n * sizeof(int)); int * v = (int *)malloc(n * sizeof(int)); for (int i = 0; i < n; i++) { scanf("%d %d",&w[i],&v[i]); } printf("%d\n",rec(0,W,w,v,n)); return 0; } int rec(i, j, w, v, n) int i, j, n; int * w, * v; { int res; if (i == n) res = 0; else if (j < w[i]) res = rec(i+1,j,w,v,n); else res = max(rec(i+1,j,w,v,n), rec(i+1,j-w[i],w,v,n)+v[i]); return res; } int max(int i, int j) { if (i > j) return i; else return j; }
但是这种方法的搜索深度是n,而且每层的搜索有需要两次分支,最坏情况下就需要O(2^n)的时间复杂度,当n比较大时就不具有可执行性了。怎么办才好呢?当我们分析程序的执行过程时,我们发现有很多值出现了多次,由于栈与栈的相互独立原理,每次计算都是与其他计算无关的。所以我们可以把第一次计算的结果给记录下来,省略掉第二次以后的重复计算试试看。
那么就模拟一个二维数组,由于一共n个物品,可以带走的总重量不超过W,所以就创建一个n*W的数组,并通过宏定义将其模拟为二维数组,将每个数第一次的计算结果记录下来。在函数调用时,如果发现结果已经计算过的话就直接使用之前计算的结果。原理已经知道了,那就来试一下吧:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define dp(M,N,length) dp[(M)*length+(N)+M] static int rec(int, int, int *, int *, int *, int); static int max(int, int); int main() { int n, W; printf("Please input n & W : \n"); scanf("%d %d",&n,&W); int sdp = n * W; int * w = (int *)malloc(n * sizeof(int)); int * v = (int *)malloc(n * sizeof(int)); int * dp = (int *)malloc(sdp * sizeof(int)); for (int i = 0; i < n; i++) { scanf("%d %d",&w[i],&v[i]); } memset(dp,-1,sdp * sizeof(int)); printf("%d\n",rec(0,W,dp,w,v,n)); return 0; } int rec(i, j, dp, w, v, n) int i, j; int * dp, * w, * v, n; { if (dp(i,j,n) >= 0) return dp(i,j,n); int res; if (i == n) res = 0; else if (j < w[i]) res = rec(i+1,j,dp,w,v,n); else res = max(rec(i+1,j,dp,w,v,n),rec(i+1,j-w[i],dp,w,v,n)+v[i]); dp(i,j,n) = res; return dp(i,j,n); } int max(int i, int j) { if (i > j) return i; else return j; }
这样就通过记忆优化和递归的方式实现了对问题的求解。
这样微小的改动到底带来了多少速度呢?对于同样的参数,只会在第一次调用时执行递归部分,第二次调用时就可以直接在数组中找到对应的值。参数的组合不超过n*W种,所以只需要O(n*W)的时间就能解决这个问题。
当然,我们可以用非递归替换递归,这样子就不需要入栈出栈操作,程序的运行速度就可以进一步提高。非递归与递归没有太大差别,所以第二个程序写出来后,就可以直接站撸非递归算法了,写法很简单,在数组里循环求解就行了。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define dp(M,N,length) dp[(M)*length+(N)+M] static int rec(int *, int *, int *, int, int); static int max(int, int); int main() { int n, W; printf("Please input n & W : \n"); scanf("%d %d",&n,&W); int sdp = n * W; int * w = (int *)malloc(n * sizeof(int)); int * v = (int *)malloc(n * sizeof(int)); int * dp = (int *)malloc(sdp * sizeof(int)); for (int i = 0; i < n; i++) { scanf("%d %d",&w[i],&v[i]); } memset(dp,0,sdp * sizeof(int)); printf("%d\n",rec(w,v,dp,n,W)); return 0; } int rec(w,v,dp,n,W) int * w, * v; int * dp; int n, W; { for (int i = 0; i < n; i++) { for (int j = 0; j <= W; j++) { if (j < w[i]) dp(i+1,j,n) = dp(i,j,n); else dp(i+1,j,n) = max(dp(i,j,n),dp(i,j-w[i],n)+v[i]); } } return dp(n,W,n); } int max(int i, int j) { if (i > j) return i; else return j; }
源代码可以直接编译运行,但是建议大家不要直接拷贝下去运行。其实你我心里也都明白,算法是思想,而不是单纯的代码,希望你们都能认真分析原理,总结题目的经验和特点,在下次遇到类似题目的时候不必上网查。
我是算法吹,以后会给大家带来更多精彩的算法。
相关文章推荐
- 常见的笔试题之mysql
- Some Advices
- iOS---选中cell时变cell颜色
- 第三次实验,本月有几天
- Appliation Loader上传App 莫名其妙的错误 The CFBundleExecutable of two bundles may not point to the same file.
- Hibernate入门
- leetcode 17 Letter Combinations of a Phone Number
- liunx centos 如何添加一个新的网卡
- HDFS源码分析之EditLogTailer
- 代理模式(java语言实现)
- 基于Hadoop2.0、YARN技术的大数据高阶应用实战(Hadoop2.0\YARN\Ma
- jenkins构建Docker 镜像(基于Jenkins的Docker镜像及Jenkins插件)
- MATLAB读取文件夹下所有图片
- C++实验3-个人所得税计算器
- 基于Hadoop2.0、YARN技术的大数据高阶应用实战(Hadoop2.0\YARN\Ma
- 多线程程序设计---1
- bind函数解析
- hdu 1847 Good Luck in CET-4 Everybody!
- Hadoop大数据零基础高端实战培训系列配文本挖掘项目
- 广东工业大学2016校赛决赛重现——E积木积水(方法据说很多)