欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

栈的练习——简单的背包问题

程序员文章站 2022-06-03 15:50:02
...

C栈的练习——简单的背包问题

-周硕 2018年3月28日

作业题3:假设有n件质量分配为w1,w2,…,wn的物品和一个最多能装载总质量为T的背包,能否从这n件物品中选择若干件物品装入背包,使得被选物品的总质量“恰好”等于背包所能装载的最大质量,即wi1+wi2+…+wik=T。若能,则背包问题有解,输出所有解;否则输出无解。

思路:这道题用来熟悉栈的基本操作,其中包含了栈的操作——初始化(InitStack)、置入元素(Push)、弹出元素(Pop)。从3月26号晚上一直到3月28号上午的程序设计课,我一直在修改找出问题,今天上午终于大功告成。背包问题有更简单的算法,这里只是用来熟悉C的语法和做栈的练习。

首先定义栈的函数——InitStack、Push、Pop:(CSDN贴C代码没有高亮真的难受..)

//背包问题(栈)
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR -1
#define OVERFLOW -2
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
//OK表示操作成功,ERROR表示操作出错,OVERFLOW表示申请空间或插入时溢出
//STACK_INIT_SIZE表示栈的初始空间,当栈的空间不够用时每次再申请STACKINCREMENT的空间
typedef struct {
	int *base;
	int *top;
	int stacksize;
}SqStack;
//定义栈结构。base为栈底指针,top为栈顶指针,stacksize为栈的空间。

这里为了便捷,直接将base和top的指针类型设置成int类型,实际上还可以:

#define ElemType int 
typedef struct{
	ElemType * top;
	ElemType * base;
	int stacksize;
}SqStack;

函数声明:

 int InitStack(SqStack *S);
 int Push(SqStack *S,int e);//压入元素e 
 int Pop(SqStack *S);
 void Package(SqStack *S,float MaxWeight,int n,float weight[STACK_INIT_SIZE])
 //MaxWeight为最大质量,n为物品个数,数组weight用于储存各物品的重量 

下面是栈的三个操作

int InitStack(SqStack *S)
{
	S->base=(int*)malloc(STACK_INIT_SIZE * sizeof(int));
	if(S->base==NULL) exit(OVERFLOW);
	S->top=S->base;
	return OK;
 } 
 
 int Push(SqStack *S,int e)
 {
 	if(S->top-S->base>=S->stacksize)
	 {
 		S->base=(int*)realloc(S->base,(STACK_INIT_SIZE+STACKINCREMENT) * sizeof(int));
		if(S->base==NULL) exit(OVERFLOW);
		S->top=S->base+STACK_INIT_SIZE; 
	 }
		*(S->top)=e;
	 S->top++;
	 return OK;
 }
 
 int Pop(SqStack *S)
 {
 	if(S->top==S->base) return ERROR;
 	S->top--;
 	return OK;
 }

这里需要注意的是栈顶指针和栈底指针的关系。当栈中没有元素时,S->top==S->base;当有元素被压入栈底时,top++,top指向栈顶元素的上一个空间。

在为栈申请空间或进行其他操作时,要尽可能考虑所有操作失败的情况,用ERROR或OVERFLOW进行返回。


main函数中进行输入和储存数据:

main()
 {
 	int n,i;//n为物品个数 
 	float MaxWeight;
 	SqStack S;
 	float data=0;
 	float weight[STACK_INIT_SIZE]; 
 	
	InitStack(&S);
 	printf("请输入背包容纳量:\n");
 	scanf("%f",&MaxWeight);
 	printf("请输入物品的个数:\n");
 	scanf("%d",&n);
 	printf("请分别输入物品质量:\n");
 
	for(i=0;i<n;i++){
		scanf("%f",&data);
		weight[i]=data;
	}
	/*printf("%f\n",MaxWeight);
	printf("%d\n",n);*/
	/*for(i=0;i<n;i++){
		printf("%f ",weight[i]);
	}*/
	printf("\n");
	Package(&S,MaxWeight,n,weight);
	return 0;
 	
 }

由于对C的语言规则还是不熟悉,main函数调试了好久,在scanf语句这里出现了问题:

float data;
/*scanf("%lf",&data);//Wrong*/
//这样数据无法储存进去,但是用%lf占位符可以输出
scanf("%f",&data);//Right
 printf("%lf",data);//Right

scanf的占位符卡的很严格,要注意这里!


Package函数:

void Package(SqStack *S,float MaxWeight,int n,float weight[STACK_INIT_SIZE])
{
	float CurrentWeight=0;
	int i=0;
	int flag=0;//flag用来判定是否有解。flag==0无解;flag==1有解。
	int* Ptr; 
	while(S->base!=S->top||i<n)
	{
		if(/*i<n&&*/CurrentWeight+weight[i]<=MaxWeight)
		{
			Push(S,i);
			CurrentWeight+=weight[i];//背包重量的变化不要忽略 
			//printf("@@");
		}
		
		if(CurrentWeight==MaxWeight)
		{
			Ptr=S->base;
			flag=1;
			while(Ptr!=S->top)
			{
				printf("%f ",weight[*(Ptr)]);
				Ptr++;
			}//end for while
			printf("\n");
			Pop(S);
			CurrentWeight-=weight[i];
			if(i==n-1)
			{
				if(S->top==S->base)
				{
					i++;
				}
				else
				{
					i=*(S->top-1);
					i++;
					CurrentWeight-=weight[*(S->top-1)];
					Pop(S);
				}
			}
			else
			{
				i++;
			 } 
		}//end for if
		
		else /*CurrentWeight<MaxWeight*/
		{
			if(i<n-1) i++;
			else//走到了最后一个元素 
			{
				if(*(S->top-1)!=n-1)//最后一个元素没有放入栈底
				{
					i=*(S->top-1);
					i++;
					CurrentWeight-=weight[*(S->top-1)];
					Pop(S);
					//printf("//");
				} 
				else//最后一个元素放入栈底 
				{
					CurrentWeight-=weight[n-1];
					Pop(S);
					if(S->base==S->top) 
					{
					i++;
					//printf("$$");
					}
					//如果栈内没有元素了,使i=n结束循环 
					else//否则再pop一个元素 
					{
						i=*(S->top-1);
						i++;
						CurrentWeight-=weight[*(S->top-1)];
						Pop(S);
						//printf("^^");	
					}
				}
			}//end for else 
		}//end for else CurrentWeight<MaxWeight
	}//end for while
	if(flag==0) printf("没有合适的装法\n");
 } //end for Package 

整个Package子函数的思路大概是这样:

栈的练习——简单的背包问题

这里为了方便调试,在每个分支中我都加入了一个打印字符,比如//、@@、&&。摸索这个程序的方法是用一组一组数据来试验,一遍一遍的走程序,直到让所有的数据都能通过。

我试验过的数据有:

3

1

3


4

2

1 2


5

3

2 1 3


6

5

2 1 3 4 6

让这些数据都能成功这个程序就已经完成了~(撒花)

程序运行结果如下:

栈的练习——简单的背包问题


栈的练习——简单的背包问题栈的练习——简单的背包问题栈的练习——简单的背包问题

完成这个程序真的花了好长好长的时间,不过也学到了不少的东西。至少对背包问题有了一个非常全面的了解。还要继续努力啊~