背包问题(帮忙写下代码加点注释)我们后天就收呢,谢谢!
【问题描述】
已知有n种物品和一个可容纳M重量的背包,每种物品i的重量为W,假定将物品i的一部分Xi放人背包就会得到PiXi的效益,这里,o≤Xi≤1,Pi>0。采用怎样的装包方法才会使装入背包物品的总效益最大 。
【问题分析】
首先建立该问题的数学模型:
极大化: (1)
约束条件: (2)
其中,(1)式是目标函数,(2)和(3)是约束条件。满足约束条件的任一集合(x1…xn)是一个可行解(即能装下),使目标函数取最大值的可行解是最优解。
例1. 考虑下列背包问题:n=3,M=20,(p1,p2,p3)=(25,24,15),(w1,w2,w3)=(18,15,10)。其中的四个可行解是
(xl,x2,x3) 总容量 总效益
? (1) (1/2,1/3,1/4) 16.5 24.25
? (2) (1,2/15,0) 20 28.2
? (3) (0,2/3,1) 20 31
? (4) (0,1,1/2) 20 31.5
每一次装入的物品应使它占用的每一单位容量获得当前最大的单位效益。这就需使物品的装人次序按pi/wi比值的非增次序来考虑。在这种策略下的量度是已装物品的累计效益值与所用容量之比。其量度标准是每次装入要使累计效益值与所用容量的比值有最多的增加或最少的减小(第二次和以后的装入可能使此比值减小)。将此贪心策略应用于例1的数据,得到解④。如果将物体事先按pi/wi的非增次序分好类,则过程greedy—knaPsack就得出这一策略下背包问题的解。
【算法描述】
procedure greedy-knaPsack(P,w,m,x,n)
real P(1:n),w(1:n),x(1:n),m,cu;//M是背包的容量大小,而x(1:n)是解向量
integer i,n;
Sort(p,w); //按P(i)/wi≥P(i+1)/w(i+1)排序的n件物品的效益值和重量,并且形成新的P(1:n)和w(1;n)。
x<- 0 //将解向量初始化为零"
cu<-m ; //cu是背包剩余容量//
for i<-1 to n do
if w(i)>cu then exit endif
xi <- 1;
cu <- cu-w(i);
repeat
if i≤n then x(i)<-cu/w(i);
endif
end greedy-knaPsack
【测试数据】
测试数据自定,要求物品的个数n,背包总容量M和每种物品的重量值及效益值。在程序运行过程中由用户从键盘输入。
【实现提示】
采用静态数组存储。
[解决办法]
作业贴,自己什么东西都不想,我劝你还是自己做,不会来讨论的好。
[解决办法]
/*此程序题目背包问题:设有n个物品,其重量分别为w1,w2,w3,…,wn,所有物品的重量之和≥背包所能放置的重量S。设计算法从中找出若干物品放入背包中,使得其重量之和正好为S。 例如,S=50, n=10, w=( 29 26 18 16 13 10 8 5 3 1)(其中每一行为一个解。每一对数中,第一个为该元素在w中的序号,第二个为元素的值): (2,26) (4,16) (7,8) (1,29) (5,13) (7,8) (1,29) (4,16) (8,5) (1,29) (3,18) (9,3) (3,18) (4,16) (5,13) (9,3) (2,26) (5,13) (7,8) (9,3) (1,29) (6,10) (7,8) (9,3) (2,26) (4,16) (8,5) (9,3) (1,29) (5,13) (8,5) (9,3) (2,26) (5,13) (6,10) (10,1) (2,26) (3,18) (8,5) (10,1) (4,16) (5,13) (6,10) (7,8) (9,3) (3,18) (4,16) (7,8) (8,5) (9,3) (3,18) (5,13) (6,10) (7,8) (10,1) (3,18) (4,16) (6,10) (8,5) (10,1) (2,26) (6,10) (7,8) (8,5) (10,1) (3,18) (5,13) (6,10) (8,5) (9,3) (10,1) */#include <stdio.h>#include <malloc.h>int package;//输出void output(int *num, int m, int array[]){ int i, sum = 0; for(i = 0; i < m; ++i) { if(*(num+i) == 1) sum += array[i]; } if(sum == package) { for(i = 0; i < m; ++i) { if(*(num+i) == 1) printf("(%d, %d) ", i+1, array[i]); } printf("\n"); }}//检测n个“1”是否全部移动到最右端//是则返回1int check(int *num, int m, int n){ int i, flag = 1; for(i = m-1; i > m-n-1; --i) { if(*(num+i) == 0) { flag = 0; break; } } return flag;}void chooseNum(int m, int n, int array[]){ int *num, i, j, count; num = (int*)malloc(sizeof(int)*m); for(i = 0; i < n; ++i)//前n个元素置1 *(num+i) = 1; if(m == n) { output(num, m, array); return; } for(i = n; i < m; ++i)//后面m-n个元素置0 *(num+i) = 0; output(num, m, array); while(1) { count = 0; //找到第一个“10”组合后将其变为“01”组合 for(i = 0; i < m-1; ++i) { if(*(num+i) == 1 && *(num+i+1) == 0) { *(num+i) = 0; *(num+i+1) = 1; break; } if(*(num+i) == 1) ++count; } //将其左边的所有“1”全部移动到数组的最左端 for(j = 0; j < i; ++j) { if(j < count) *(num+j) = 1; else *(num+j) = 0; } output(num, m, array); if(check(num, m, n) == 1) break; } }int main(){ int n, i; int array[10] = {29, 26, 18, 16, 13, 10, 8, 5, 3, 1}; n = 10; package = 50; for(i=1; i<=n; ++i) chooseNum(n, i, array); return 0;}