背包问题使用顺序栈的方法解决。
1.问题描述
有一个背包,能盛放的物品总重量为t,现有n件物品,其重量分别为w1、w2、...、wn,问能否从这n件物品中选择若干件装满这个背包?
2.问题分析
装包过程是将n件物品排成一列,依次选取;若选取的物品装入后,背包内物品的总重量不超过背包总重量时,则装入;否则放弃这件物品的选择,选择下一件物品试探,但如果在剩余的物品中找不到“合适”的物品,则说明“刚刚”装入背包的物品“不合适”,应该将其取出,继续再从未装入的物品中选取。重复此过程,直至装满背包为止。
背包中物品的装入与取出具有“后进先出”的特点,采用顺序栈来表示背包中的物品。
3.算法描述
(1)依次选取每一个物品i,若t>w[i],表示该物品可选,则将下标i进栈;同时,从t中减去该物品的重量w[i]。
(2)当t等于0时,背包刚好装满。
(3)当无物品可选时,需退栈。退栈时,要将相应物品的重量加到t中。
流程图如下:
#define MAXSIZE 50i #include<stdio.h> typedef struct node{ int data[MAXSIZE]; int top; }Node; /*函数声明*/ void InitStack(Node *p); //初始化栈(置空栈) void PushStack(Node *p, int n); //进栈 int PopStack(Node *p); //退栈 int IsEmpty(Node *p); //判栈空 void Bag(int things, int *weight, int bag); //背包函数 /*主函数*/ int main(void){ int things,weight[MAXSIZE],bag,s,i; printf("Please enter the number of the things:"); scanf("%d",&things); printf("\nPlease enter their weight in order:"); for(i=0,s=0;i<things;i++){ scanf("%d",&weight[i]); s=s+weight[i]; } printf("\nTheir weight in total is %d.",s); printf("\nPlease enter the weight that the bag can contain(under %d):",s); scanf("%d",&bag); Bag(things, weight, bag); } /*函数1*/ void InitStack(Node *p){ p->top=-1; } /*函数2*/ void PushStack(Node *p, int w){ p->data[++p->top]=w; } /*函数3*/ int PopStack(Node *p){ int v; v=p->data[p->top--]; return v; } /*函数4*/ int IsEmpty(Node *p){ return (p->top==-1); } /*背包函数*/ void Bag(int things, int *weight, int bag){ Node Stack; int i=0,j=0,k; InitStack(&Stack); //初始化栈 do{ while(i<things && bag>0){ //装包过程 if(bag>=weight[i]){ PushStack(&Stack, i); bag=bag-weight[i]; } i++; } if(bag==0){ j++; printf("OK solution No.%d:",j); for(k=0;k<=Stack.top;k++){ printf("%d ",weight[Stack.data[k]]); } printf("\n"); } i=PopStack(&Stack); //标记1 让i记录下栈顶的值 //从背包中取出最后装进的物品 bag=bag+weight[i]; i++; //标记2 下次循环开始时从这次的最后一个物品的下一个物品开始拿 }while(!IsEmpty(&Stack)||i<things); }
data记录的是被放入包里的物品的下标,top指示栈顶的位置。
标记1和标记2让i在每次循环开始的时候能将第一个放入包里的东西往后移一个,也就是第一轮一开始放进包里的是第一个物品,如果第二轮还是的话,之后的步骤就和第一轮一模一样了,所以要往后移一个,第二轮要从第二个物品开始放。以此类推,最后一轮只把最后一个物品放入包里,不满足条件(包内剩余空间不等于0)的话,下一轮i就大于物品数量(不满足大循环条件)了,退栈以后栈空(因为这一轮只放了一个物品进包里),于是跳出大循环,结束程序。
最重要的还是用i记录了栈顶的值。还有大循环的条件:栈非空,这里十分巧妙,它防止了在某一轮(不是最后一轮)试过最后一个物品能不能放进背包之后,这时候i大于物品数量(因为i在小循环那里加了1),如果没有栈非空的条件的话,就跳出大循环了。这样的情况,就是只能放第一轮物品。我一开始就有这样的疑惑:为什么这个程序可以进行许多轮并输出正确答案,一开始我以为它只能进行第一轮。