0-1背包问题:

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这个问题的特点是:每种物品只有一件,可以选择放或者不放。

算法基本思想:

利用动态规划思想 ,子问题为:f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

其状态转移方程是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}   //这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。

解释一下上面的方程:“将前i件物品放入容量为v的背包中”这个子问题,如果只考虑第i件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题,即1、如果不放第i件物品,则问题转化为“前i-1件物品放入容量为v的背包中”;2、如果放第i件物品,则问题转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”(此时能获得的最大价值就是f [i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i])。则f[i][v]的值就是1、2中最大的那个值。

(注意:f[i][v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。所以按照这个方程递推完毕后,最终的答案并一定是f[N] [V],不一定是唯一的最大值,也可以是f[N][0..V]的最大值。

以上概念和算法基本思想来源于blog:http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html

以下为算法代码:是个不断优化的过程。首先(1)给出了递归和迭代的思想,(2)然后对迭代的算法代码进行空间优化。(3)然后给出了该问题的变种:恰好装满背包和完全背包问题。

(1)递归方法:

// 0-1 背包问题递归方法  回溯求最佳解
int packageRecursion(int c, int n)
{if(n == N) return (c < weight[n])? 0 : value[n];if(c < weight[n]) return packageRecursion(c, n+1);return max(packageRecursion(c, n+1), packageRecursion(c - weight[n], n+1) + value[n]);
}

(2)迭代方法:

//  0-1 背包问题迭代方法
void packageIteraction()
{int **F;F = new int*[N+1];   // 申请内存空间int i;for(i = 0; i < N+1; i++){F[i] = new int [C+1];}int j;for(i = 0 ; i < N+1; i++){for(j = 0; j < C+1; j++){F[i][j] = 0;              // 初始化}}// 正好装满背包 加上这几行/*for(i = 0 ; i < N+1; i++){for(j = 1; j < C+1; j++){F[i][j] = min;             }}*/for(i = 1; i < N+1; i++){for(j = weight[i]; j < C+1; j++){F[i][j] = F[i-1][j] > (F[i-1][j-weight[i]]+value[i])? F[i-1][j] : (F[i-1][j-weight[i]]+value[i]);}}if(F[N][C] > 0){cout << "the opt value:" << F[N][C] << endl; j = C;                //  回溯 遍历出所选择的节点for(i = N; i >= 1; i--){if(F[i][j] == (F[i-1][j-weight[i]]+value[i])){cout << i << "weight=: " << weight[i] << " value=: "<< value[i] << endl;j = j - weight[i];}}}else{cout << "not finde the opt value" << endl;}for(i = 0; i < N+1; i++)     // 释放内存空间{delete[] F[i];}delete[] F;}

(3)迭代方法优化空间复杂度:

      以上迭代方法的时间复杂度和空间复杂度都是O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。此时的做法是用F[V]来存储当前获得的价值的最大值,第二个for循环反向遍历。

//  将j=V:weight[i] 只用一维数组保存就可以了,降低了空间复杂度
void packageIteractionOpt()
{int *F;F = new int[C+1];   // 申请内存空间int i;for(i = 0; i < C+1; i++)F[i] = 0;// 正好装满背包 加上这句/*for(i = 1; i < C+1; i++)      F[i] = min; */for(i = 1; i < N+1; i++){for(int j= C; j >= weight[i]; j--){F[j] = F[j] > (F[j-weight[i]]+value[i])? F[j] : (F[j-weight[i]]+value[i]);}}cout << F[C] << endl;delete []F;     // 释放内存空间
}

注意:上面要从后往前遍历,前面的都是上一次的结果,如果从前往后遍历,前面的是这次的结果。

Main函数代码:

int main()
{cin >> C >> N;weight = new int[N+1];value = new int[N+1];value[0] = 0;weight[0] = 0;int i;for(i = 1; i <= N; i++)cin >> value[i] >> weight[i];//cout << packageRecursion(C, 1) << endl;   // 递归方法//packageIteraction();packageIteractionOpt();        // 在同等条件下,降低了空间复杂度delete []weight;delete []value;//cout << int(0x80000000) << endl;     // int 中最小值//cout << int(0xfffffffff) << endl; ;            // int中-1return 0;
}

输出结果:

(4)变种

       1:恰好装满背包

在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[V]是一种恰好装满背包的最优解,即背包中存有的价值,不是背包的容量。如果不能恰好满足背包容量,即不能得到f[V]的最优值,则此时f[V]=-∞,这样就能表示没有找到恰好满足背包容量的最优值。

以上代码中可以将注释的部分加上就可以了。其中int类型中最小值为0x80000000.

       2:完全背包

此问题来源于hihoCoder中:链接,即将背包只能兑换一次或者不兑换改为可以兑换无数次

/*完全背包问题 http://hihocoder.com/contest/hiho7/problem/1 */
#include <iostream>
#include <memory.h>    //A题时候使用memset需要添加这个库函数
using namespace std;
int main()
{int N, M;while(cin >> N >> M){int need, value;int *F = new int[M+1];int i, j;memset(F, 0, sizeof(int)*(M+1));//for(i = 0; i < M+1; i++)//F[i] = 0;for(i = 1; i < N+1; i++){cin >> need >> value;for(j = need; j<=M; j++){F[j] = F[j] > (F[j-need]+value)?F[j]:(F[j-need]+value);}   }// 错误代码/*如样例 2 10 2 3 3 5*//*int k;for(i = 1; i < N+1; i++){cin >> need >> value;for(j = M; j>=need; j--){if(need == 0) k = 0;else k = j/need;   //  F[j] = F[j] > (F[j-k*need]+k*value)?F[j]:(F[j-k*need]+k*value);  }}*/cout << F[M] << endl;delete []F;}
}// 测试输入
/*5 1000
144 990
487 436
210 673
567 58
1056 897*/// 输出
// 5940
参考文献
1: http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html
2:http://www.cnblogs.com/findumars/archive/2012/01/28/2330647.html
3:http://blog.csdn.net/shaopengfei/article/details/1473020
作者:小村长  出处:http://blog.csdn.net/lu597203933 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:小村长zack, 欢迎交流!)

附腾讯笔试题

“背包题目”的基本描述是:有一个背包,能盛放的物品总重量为S,设有N件物品,其重量分别为w1,w2,…,wn,希望从N件物品中选择若干物品,所选物品的重量之和恰能放进该背包,即所选物品的重量之和即是S。递归和非递归解法都能求得“背包题目”的一组解,试写出“背包题目”的非递归解法

分析:

(1)递归解法直接用回溯就可以了,我们看代码:

// 回溯void backTracking(int depth, int maxDepth, vector<int> temp, vector<int> vec, const int &S){if(depth == maxDepth){    // 递归结束int sum = 0;vector<int> tmp;for(int i = 0; i < maxDepth; i++){if(temp[i] == 1){sum += vec[i];tmp.push_back(i);}}if(sum == S)result.push_back(tmp);return;}backTracking(depth+1, maxDepth, temp, vec, S);    // 没有取depth的元素时的递归temp[depth]=1;backTracking(depth+1, maxDepth, temp, vec, S);   // 取depth的元素时的递归}

(2)非递归求出所有的解,我们可以采用位操作:

// 找出所有的解void package(vector<int> vec, int S){int n = vec.size();int s = 1 << n;for(int i = 1; i < s; i++){int sum = 0;vector<int> temp;for(int j = 0; j < n; j++){if((i & (1<<j)) != 0){ sum += vec[j];temp.push_back(j);}}if(sum == S) result.push_back(temp);}}

但时间复杂度较高为O(s*2^n)

(3

动态规划求一组解,我们用f[i][j]用来表示前i件物品随意选择一些物品能够恰好装满容量为j的背包的可行性=1,不可行则为0;如果j < v[i]:f[i][j] = f[i-1][j] ,表示第i个物品的重量已经超过最大重量j了,则结果变为了前i-1件物品能否恰好装入容量为j的背包的可行性即等于f[i-1][j];

如果j>=v[i],此时就可以选择装或者不装第i件物品了,如果装入,则它的可行性就变为了前i-1前物品是否能恰好装入j-vec[i]容量的背包中可行性了;选择不装,则它的可行性变为了前i-1物品能否恰好装入容量为j的背包中可行性了,两者有一个可行,表明前i件物品就能恰好装入容量为j的背包中。f[i][j] = f[i-1][j]||f[i-1][j-vec[i]]

初始化:f[i][0]=1(i=0,1,2,,..n) 其它的f[i][j]都等于0

找出最终的一组解,用回溯就可以了。如果存在可行解,那么f[n][S]一定为1

代码:

// f[i][j]用来表示前i件物品随意选择一些物品能够恰好装满j的可行性=1,不可行则为0,// 如果j < v[i]:f[i][j] = f[i-1][j]  表示肯定不能取第i件物品了,那么就变为i-1件能否恰好装入容量为j的背包的可行性了。//  否则,f[i][j] = f[i-1][j]||f[i-1][j-v[i]]  表示装第i件物品,问题变为前i-1件物品是否能恰好装进j-vec[i]中的可行性或者不装第i件物品,则问题变为前i-1件能否恰好装入容量为j的背包的可行性f[i-1][j],相或即为结果// 初始化,除了f[0][j]为1,其余全部0// 最终的结果f[n][S]为1表明存在一组解使得n解物品肯定能恰好装满容量为j的背包void packageInteration(vector<int> vec, int S){int n = vec.size();int **f = new int*[n+1];for(int i = 0; i < n+1; i++){f[i] = new int[S+1];memset(f[i], 0, sizeof(f[i])*(S+1));}for(int i = 0; i < n+1; i++)f[i][0] = 1;   // 初始化for(int i = 1; i < n+1; i++){for(int j = 1; j < S+1; j++){if(j >= vec[i-1])f[i][j] = (f[i-1][j] || f[i-1][j-vec[i-1]]);else f[i][j] = f[i-1][j];}}for(int i = 1; i < n+1; i++){for(int j = 1; j < S+1; j++){cout << f[i][j] << " ";}cout << endl;}// 回溯求结果int j = S;for(int i = n; i > 0; i--){if(j >= vec[i-1]){if(f[i][j-vec[i-1]] == 1){   // 为0 表示可行res.push_back(i);j = j - vec[i-1];}}}}

0-1背包问题及变种相关推荐

  1. 0/1背包问题——动态规划方法

    1.定义 动态规划:把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解. 2.求解步骤 (1)找到状态转化条件 (2)归纳状态转移方程 (3)定义初始条件值 3.实例解析--0/1背包 ...

  2. 0/1背包问题-----回溯法求解

    问题描述 有n个物品和一个容量为c的背包,从n个物品中选取装包的物品.物品i的重量为w[i],价值为p[i].一个可行的背包装载是指,装包的物品总重量不超过背包的重量.一个最佳背包装载是指,物品总价值 ...

  3. 0/1背包问题-----动态规划求解

    问题描述 有n个物品和一个容量为c的背包,从n个物品中选取装包的物品.物品i的重量为w[i],价值为p[i].一个可行的背包装载是指,装包的物品总重量不超过背包的重量.一个最佳背包装载是指,物品总价值 ...

  4. 【动态规划】0/1背包问题

    问题 H: [动态规划]0/1背包问题 时间限制: 1 Sec  内存限制: 64 MB 提交: 152  解决: 95 [提交] [状态] [讨论版] [命题人:admin] 题目描述 张琪曼和李旭 ...

  5. 分枝定界法解0/1背包问题

    分枝定界法解0/1背包问题 关键词:分支定界.0-1背包 分枝定界法简介 分枝定界法按照树形结构来组织解空间,展开节点后,有两种策略: 策略一.把节点加入 FIFO 队列当中: 策略二.把节点加入到堆 ...

  6. 回溯法 —— 求解0/1背包问题(剪枝)

    0/1背包问题 题目描述: 有n个重量分别为w1,w2,-,wn的物品(物品编号为1~n),它们的价值分别为v1,v2,-,vn,给定一个容量为W的背包.设计从这些物品中选取一部分物品放入该背包的方案 ...

  7. 令人头疼的背包九讲(1)0/1背包问题

    点击上方"Jerry的算法和NLP",选择"星标"公众号       重磅干货,第一时间送达 背包问题是一个经典的动态规划模型.它既简单形象容易理解,又在某种程 ...

  8. 多米诺骨牌——变形版0,1背包问题

    多米诺骨牌--变形版0,1背包问题 1.题目描述 2.问题分析 3.算法源码 1.题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的上方块中点数之和记为S1,下方块中点数之和 ...

  9. 0/1背包问题(蛮力法)

    问题描述: 给定n个重量为{w1,w2,w3,....,wn}.价值为{v1,v2,v3,...,vn}的物品和一个容量为C的背包,0/1背包问题是求解这些物品中的一个最有价值的子集,并且要能够装到背 ...

  10. 分枝限界法求解0/1背包问题

    问题描述 有n个重量分别为{w1,w2,-,wn}的物品,它们的价值分别为{v1,v2,-,vn},给定一个容量为W的背包. 设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中 ...

最新文章

  1. 延毕率逐年上升!我国博士研究生累计招生已近150万!
  2. Windows上打开大文件的工具
  3. 子类继承父类,并重写父类方法
  4. 【Python】创建数组[[0]*n]*m与[[0 for _ in range(n)] for _ in range(m)]的区别
  5. C# ADO.NET
  6. PAT 1114 Family Property 并查集
  7. GitHub Actions 部署 VuePress 文档
  8. screen乱码问题
  9. asp.net mvc Autofac 依赖注入框架 使用
  10. day14:磁盘管理df/du/fdisk/parted命令
  11. HTML5+css3 的开心网游戏页面
  12. 秀球技:倒和其他无用
  13. python从字符串中提取数字
  14. 《摩根写给儿子的32封信》 03 企业家的资质
  15. 抖音小程序怎么注册?一文看懂抖音小程序注册开发流程
  16. java抽象类重载_012 JAVA 抽象类、接口、String类的基础了解
  17. 美国高防服务器:恒讯科技为您解答TCP/IP的攻击原理
  18. 使用git时出现fatal: Authentication failed for
  19. Windows窗口消息介绍
  20. 【Azu Cloud云盘 V1.0】基于python django写的云盘 全开源分享

热门文章

  1. 2021年广州市软件示范平台入库申报时间及条件
  2. 所有初始化参数说明(转)
  3. 骁龙778g和天玑810哪个好 骁龙778g和天玑810差别有多大
  4. 阿里云技术专家解读 | 行业首创电商搜索模板
  5. 小程序 多张图片上传(源码分享+实现分析)
  6. 真格基金、腾讯为什么都看好这家人工智能初创公司?
  7. 还在使用if进行空判断?
  8. SOME/IP开源库Vsomeip E2E保护实现分析
  9. 电脑关闭疑难解答_如何使用Office 365的疑难解答工具解决常见问题
  10. 年薪500万,元宇宙画的饼能落地吗?