相信大家和我一样,在刚开始写背包问题时因为不会动态规划而无从下手,其实简单背包问题真的很简单,只要你仔细阅读这篇文章,就会很快解决~
题目
给定n种物品和一背包。物品i的重量(体积)是w~i,其价值为v~i,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大。
要求:
* 系统给出的第一个数字是背包容量,第二个数字是物品件数,然后分别给出每件物品的重量,再分别给出每件物品的价值。
* 请依次输出装入背包的物品序号,总重量,总价值。
1.贪心算法
首先我们想到的是贪心算法,用单位重量的价值的大小来判定此物体是否要放入包中,但是贪心算法不一定是最优解,为什么呢?
比如一个背包的最大容量是100
编号 1 2 3 4
一些物体的重量是50 10 60 40
他们的重量分别是50 2 30 40
显而易见单位重量价值排序为1>4>3>2
在贪心算法下,我们在取完1和4以后,在取3时发现超过容量,就不继续往后取u,但是实际上我们的2号物品重量刚好不超,并且还增加了总价值,这就是贪心算法的缺陷所在,他的解并非是最优解
那么贪心算法无法解决,我们就来暴力枚举,枚举法(穷举法)得出来的一定是最优解哦~
2.枚举法
**算法描述(伪代码):**
for (i=0 to 2^n-1)
{
将 i 对应的二进制数按位存放到数组Y中;
尝试按照装包方案y进行装包,则背包中物品的重量为CurWeight, 价值为CurValue;
如果CurWeight>C,则丢弃该方案,继续尝试下一种方案;否则,若该方案优于以前的方案,即如果CurValue>Value,则暂存该方案:即X=Y,Value=CurValue, Weight= CurWeight。 继续循环;
}
看了上述代码,可能你还会有些迷惑,简单的说,就是利用0与1的性质,取对应着乘以1,不取就对应着乘以0
1.定义W和V数组存放重量和价值,输入背包容量等信息
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<math.h> int main() { int c,k,n,i,j,m,V,W,MaxV=0,MaxW=0,count; scanf("%d %d", &c, &n); int w[1000] = { 0 }; int v[1000] = { 0 }; int arr[1000] = { 0 }, x[1000] = { 0 }; for (i = 0; i < n; i++) { scanf("%d", &w[i]); } for (i = 0; i < n; i++) { scanf("%d", &v[i]); }
2.通过枚举取与不取的可能有n的2^n次方种可能,(根据排列组合,n个物体取或不取有2^n种可能性),物品的取与不取正好对应着将0和1分别排列组合的放入一个数组里面。
for (i = 1; i < pow(2, n); i++) { m = n-1; //枚举取与不取,方法就是让对应的价值和重量乘以1或0,此时将0与1放在和价值和重量对应顺序的位置,方便相乘 for (j = 0; j < n; j++) { arr[m--] = (1&(i>>j)); }
这里的(1&(i>>j))指的是将i右移j位2进制位以后与1的二进制位取并
3.将每一种1到2^n种可能性比较大小,先判断是否超过容量,在判断是否是最大价值,在每一次放入背包时一定要记得清空背包以便于下一次计算哦~
清空背包以便于下一次计算 W = 0; V = 0; //将东西放入背包 for (k = 0; k < n; k++) { W += arr[k] * w[k]; V += arr[k] * v[k]; 背包现在的重量和价值 } //比较是否超过容量 if (W <= c) { //比较是否是最大价值 if (V > MaxV) { MaxV = V; MaxW = W; count = 0; for (int h = 0; h < n; h++) { //应题目要求将序号存起来方便打印 if (arr[h] == 1) { x[count++] = h+1; } } } } }
4.打印出结果即可
for (i = 0; i < count; i++) { printf("%d ", x[i]); } printf("%d %d", MaxW, MaxV); }
完整代码
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<math.h> int main() { int c,k,n,i,j,m,V,W,MaxV=0,MaxW=0,count; scanf("%d %d", &c, &n); int w[1000] = { 0 }; int v[1000] = { 0 }; int arr[1000] = { 0 }, x[1000] = { 0 }; for (i = 0; i < n; i++) { scanf("%d", &w[i]); } for (i = 0; i < n; i++) { scanf("%d", &v[i]); } for (i = 1; i < pow(2, n) - 1; i++) { m = n-1; for (j = 0; j < n; j++) { arr[m--] = (1&(i>>j)); } W = 0; V = 0; for (k = 0; k < n; k++) { W += arr[k] * w[k]; V += arr[k] * v[k]; } if (W <= c) { if (V > MaxV) { MaxV = V; MaxW = W; count = 0; for (int h = 0; h < n; h++) { if (arr[h] == 1) { x[count++] = h+1; } } } } } for (i = 0; i < count; i++) { printf("%d ", x[i]); } printf("%d %d", MaxW, MaxV); }
以上就是简单01背包问题的解决方法,如果大家有更简单更优的解法,欢迎大家在评论区讨论哦~
写博客不易,希望大家多多关注和点赞,我会一直更新我的博客和大家一起进步的!