分支限界法解决装载问题之FIFO队列方式的总结

1.先说一下顺序队列来建立数据结构

 

/************************************************************************
顺序队列(循环队列)实现FIFO分支限界法--装载问题把数据结构换了一下,
仅仅把之前的链队列换成了循环队列。其余的函数基本没有变,还有main()函
数根本就没变,只是EnQueue(), Add(), DeQueue()这些涉及到E和bestE的函
数中的某些函数参数由QUEUE *类型变成了int类型。函数调用中的参数根本就
没变,也不用变。在原来的链队列程序中,数据结构定义按照节点来的,每个
节点中有*parent, weigth, LChild, *next。然后一个队列是很多个节点,每次
入队或者出队通过加入含有这些类型元素的节点或者删除,E和bestE是独立的,
不依赖于链队列LQ的具有以上所说类型的节点,每次用来存放某些节点的地址,
只要找到了某个节点地址,就可以调用这个节点里面的*parent, weigth, LChild
等元素,这是链队列的来实现的思想。这里我采用循环队列来实现,顺序队列也
好,循环队列也好,都是顺序的存储方式,即将要存储的数据存到数组里面(数组
是顺序的)。所以思想就跟之前的链队列实现的思想有所不同,原来每个节点有一
个parent, weight,LChild之类的,但是如今只有一个节点SQ。元素的寻找是通过
下标值来寻找也就相当于模拟链队列里面的节点地址,这个下标值可以模拟成地
址。so,weight, parent, LChild每个都是一个数组weight[], parent[], LChild[];
比如链队列中的E->weight表示的是E所指向的那个节点的weight,在循环队列
里面就要用weight[E]来实现了,在链队列中,E是QUEUE*型的,表示的是队列
节点的那种类型,然而在顺序队列里面,就定义成了int型,表示的是下标值,从
1开始(0表示加入空节点weight值为-1时)。再比如,原来的链队列程序中
bestx[j] = bestE->LChild ;bestE = bestE ->parent ;换成了下面的:
bestx[j] = Q->LChild[bestE] ;	bestE = Q->parent[bestE] ;so,可以仔细体味
一下这两种方式的不同。
其实还有一种构建数据结构的方式,也是循环队列或者顺序队列,总是是用数组来
存储元素的。但是那种方式和链队列的思想基本上是一样,按照开辟节点的方式,
唯一不同的是链队列的节点之间不是顺序的,而是通过指针连起来的,而此种方式
确实开辟一个顺序的节点空间Q[100];见下面
struct
{
	int weight;
	int LChild;
	int parent;
}Q[MAXSIZE];
int front, int rear;
这个和链队列方式基本一样weight, LChild, parent都是节点内部的元素。而front, 
rear都是独立的元素,用来指向节点。仔细体味三种队列构建方式的异同。
*******************************************************************************/
#include
#include

#define	MAXSIZE	100
typedef struct 
{
	int weight[MAXSIZE];
	int LChild[MAXSIZE];
	int parent[MAXSIZE];
	int front, rear;
}SEQUEUE;

//只有个全局变量:*bestx,bestw。
int *bestx ;
int bestw = 0 ; // 目前的最优值

void InitQueue(SEQUEUE *SQ)
{
	SQ->front=SQ->rear=0;
	for(int i=0;iparent[i]=-1;
	}
}

int Empty(SEQUEUE *SQ)
{
	if(SQ->rear==SQ->front)
		return 1;
	else
		return 0;
}

int Add(SEQUEUE *SQ, int w, int E, int l)
{
	if((SQ->rear+1)%MAXSIZE==SQ->front)
	{
		printf("队列满了!\n");
		return 0;
	}
	SQ->rear=(SQ->rear+1)%MAXSIZE;
	SQ->weight[SQ->rear]=w;
	SQ->LChild[SQ->rear]=l;
	SQ->parent[SQ->rear]=E;
	return 1;
}

int DeQueue(SEQUEUE *SQ, int *E)
{
	if(Empty(SQ))
	{
		printf("队列空了!\n");
		return 0;
	}
	SQ->front=(SQ->front+1)%MAXSIZE;
	*E=SQ->front;
	return 1;
}

void EnQueue(SEQUEUE *SQ, int wt, int i, int n , int E , int *bestE , int ch)
{
	if(i==n)
	{
		if(wt==bestw)
		{
			*bestE = E;//bestE只有i==n时候,也就是叶子的时候,才用得到bestE,因为为了追溯路径,靠bestE=bestE->parent;也就是说一旦到了叶子节点,说明搜索已经结束
            bestx[n] = ch;
		}
        return;
	}

	Add(SQ, wt , E, ch); // 不是叶子
}

/*
int GetHeadQueue(SEQUEUE *SQ, int *x)
{
	if(Empty(SQ))
	{
		printf("队列空了!\n");
		return 0;
	}
	*x=SQ->data[(SQ->front+1)%MAXSIZE];
	return 1;
}
*/

int MaxLoading(int w[], int c, int n)//求最大装载函数
{
	int err ; //返回值
	int i = 1; // 当前扩展结点的层
	int cnt=0;
	int Ew = 0; // 当前扩展结点的权值
	int r = 0 ; //剩余集装箱重量
	int flag=0;
	int E=0;
	int bestE=0;
	SEQUEUE	 *Q=(SEQUEUE *)malloc(sizeof(SEQUEUE)); // 活结点队列

	for(int j =2; j<=n; j++)
	{
          r += w[j];
	}

	bestw = 0; // 目前的最优值

	InitQueue(Q);
	err=Add(Q, -1, 0, 0);
	if(!err)
	{
		return 0 ;
	}

	while (true) 
	{ 
		int wt = Ew + w[i] ;
		if (wt <= c)   // 检查左孩子结点
		{
			if(wt>bestw) 
				bestw = wt ;// x[i] = 1
			EnQueue(Q, Ew + w[i], i , n , E , &bestE , 1);
			flag=1;
		
			/******这部分剪枝的内容也可以放在这个里面,当然下面就只有if(!flag),而没有else了***
			if(Ew+r>=bestw&&ibestw&&i当前最优值bestw
					//(bestw一般来讲就是当前的右子树的同一级的左节点的值),如果不大于,说明本级的下面几级的子树即使全部取左子树(每个物品都要)
					//这样也不如当前的值bestw大,所以此右子树就不必要了,所以对右子树进行判断剪枝(满足if里面的条件就不剪枝,右孩子入队)。
		}
		DeQueue(Q, &E);
		if (E!=0&&Q->weight[E] == -1)  //我觉得这个地方E!=NULL没啥用,直接if(E->weight==-1)就行
		{// 到达层的尾部
			if (Empty(Q)) 
			{
				break;
			}
			if(iweight[E];

	}

	for(int j = n-1; j>0; j--)//构造当前最优解
	{
		bestx[j] = Q->LChild[bestE] ;
		bestE = Q->parent[bestE] ;
	}
    return 0 ;  
} 

int main()
{
	int n =0 ; 
	int c = 0 ;
	int i = 0 ;
	int* w ;
	FILE	*in , *out ;
	in = fopen("input4.txt" , "r") ;
	if(in==NULL)
	{
		printf("没有输入输出文件\n") ;
		return 1 ;
	}

	fscanf(in , "%d" , &n) ;
	fscanf(in , "%d" , &c) ;
	w = (int*)malloc(sizeof(int)*(n+1)) ;
	bestx = (int*)malloc(sizeof(int)*(n+1)) ;
	for(i =1 ; i<=n ; i++)
	{
		fscanf(in , "%d" , &w[i]) ;
	}

	MaxLoading(w , c , n) ;

	out = fopen("output.txt" , "w") ;
	for(i=1 ; i<=n ; i++)
	{
		fprintf(out , "%d\t" , bestx[i]) ;
	}
	fprintf(out , "\n") ;
	fclose(in);
	fclose(out);
	return 0 ;
}


二.再说一下链队列来建立数据结构

/************************************************************
二重指针方式解决FIFO队列装载问题v0.3版本,全部采用
局部变量+二重指针+正规熟悉的队列构建方式+正确的剪枝算法
输入:在input.txt中输入n,c,w[1], w[2], ......, w[n]。
输出:输出到output.txt中bestx[1], bestx[2], ......, bestx[n]的值
		为表示w[i]被选中,为表示没有被选中。
数据结构:队列。应用了自己定义的规范型的通用的队列实现方式,
			   并且灵活做了个性化处理,比如*parent, LChild等
*************************************************************/
#include
#include

typedef struct Qnode{
    struct Qnode *parent; // 指向父结点的指针
	struct Qnode* next ;
    int LChild;        // 左儿子标志
    int weight;       // 结点所相应的载重量
}QTYPE;
/*注意:再定义*E, *bestE的时候直接用QTYPE定义,因为E和bestE都是QTYPE型的,不用定义成LinkQUEUE型的。E和bestE是个游离的节点,与LinkQUEUE *Q的Q没直接关系,不依赖与Q,是自由的,但是front 和rear是在LinkQUEUE里面定义的,虽然是QTYPE类型的,但是依赖于Q。Q->front, Q->rear.
*/
typedef struct	
{					//
	QTYPE	*front,	*rear;
}LinkQUEUE;					// 

//只有2个全局变量:*bestx,bestw。
int *bestx ;
int bestw = 0 ; // 目前的最优值

int InitQueue(LinkQUEUE *LQ)//初始化队列
{
	QTYPE	*p;
	p=(QTYPE *)malloc(sizeof(QTYPE));
	if(!p)
	{
		printf(" 分配空间失败!\n");
		return 0;
	}
	p->next=NULL;
	p->weight=-1;
	p->parent=NULL;
	LQ->front=LQ->rear=p;
	return 1;
}

int Add(LinkQUEUE *LQ,  int w, QTYPE *E, int l)//入队的原子操作
{
	QTYPE *q;
	q=(QTYPE *)malloc(sizeof(QTYPE));
	if(!q)
	{
		printf("分配空间失败!\n");
		return 0;
	}
	q->weight=w;//
	q->next=NULL;
	q->parent=E;//
	q->LChild=l;//
	LQ->rear->next=q;
	LQ->rear=q;
	return 1;
}

int IsEmpty(LinkQUEUE  *LQ)//判断对空
{
	return (LQ->front==LQ->rear?1:0);
}

/*出队,出队的节点指针赋给*E,通过二重指针方式来修改传递的参数*/
int Delete(LinkQUEUE  *LQ, QTYPE **E)
{
	QTYPE *tmp=NULL;
	if(IsEmpty(LQ))
	{
		printf("队空!\n");
		return 0;
	}
	tmp=LQ->front->next;
	LQ->front->next=tmp->next;
	if(LQ->front->next==NULL)
		LQ->rear=LQ->front;
	*E=tmp;
	//free(tmp);
	return 1;
}
/*入队的封装操作通过二重指针方式来修改传递的参数*/
void EnQueue(LinkQUEUE *LQ, int wt,int i, int n , QTYPE  *E ,QTYPE  **bestE , int ch) {
	if(i==n)
	{
		if(wt==bestw)
		{
			*bestE = E;//bestE只有i==n时候,也就是叶子的时候,才用得到bestE,因为为
//了追溯路径,靠bestE=bestE->parent;
//也就是说一旦到了叶子节点,说明搜索已经结束
            bestx[n] = ch;
		}
        return;
	}

	Add(LQ, wt , E, ch); // 不是叶子
}


int MaxLoading(int w[], int c, int n)//求最大装载函数
{
	int err ; //返回值
	int i = 1; // 当前扩展结点的层
	int cnt=0;
	int Ew = 0; // 当前扩展结点的权值
	int r = 0 ; //剩余集装箱重量
	int flag=0;
	LinkQUEUE	*Q=(LinkQUEUE *)malloc(sizeof(LinkQUEUE)); // 活结点队列
	QTYPE *E=NULL; 
	QTYPE	*bestE=NULL;

	for(int j =2; j<=n; j++)
	{
          r += w[j];
	}

	bestw = 0; // 目前的最优值

	InitQueue(Q);
	err=Add(Q, -1, NULL, 0);
	if(!err)
	{
		return 0 ;
	}

	while (true) 
	{ 
		int wt = Ew + w[i] ;
		if (wt <= c)   // 检查左孩子结点
		{
			if(wt>bestw) 
				bestw = wt ;// x[i] = 1
			EnQueue(Q, Ew + w[i], i , n , E , &bestE , 1);
			flag=1;
		
	/***这部分剪枝的内容也可以放在这个里面,当然下面就只有if(!flag),而没有else了
			if(Ew+r>=bestw&&ibestw&&i当前最优值
//bestw(bestw一般来讲就是当前的右子树的同一级的左节点的值),如果不
//大于,说明本级的下面几级的子树即使全部取左子树(每个物品都要)
		     //这样也不如当前的值bestw大,所以此右子树就不必要了,所以对右子树进
//行判断剪枝(满足if里面的条件就不剪枝,右孩子入队)。
		}
		Delete(Q, &E);
		if (E!=NULL&&E->weight == -1)  //我觉得这个地方E!=NULL没啥用,直接
//if(E->weight==-1)就行
		{// 到达层的尾部
			if (IsEmpty(Q)) 
			{
				break;
			}
			if(iweight ;

	}

	for(int j = n-1; j>0; j--)//构造当前最优解
	{
		bestx[j] = bestE->LChild ;
		bestE = bestE ->parent ;
	}
    return 0 ;  
} 

int main()
{
	int n =0 ; 
	int c = 0 ;
	int i = 0 ;
	int* w ;
	FILE	*in , *out ;
	in = fopen("input.txt" , "r") ;
	if(in==NULL)
	{
		printf("没有输入输出文件\n") ;
		return 1 ;
	}

	fscanf(in , "%d" , &n) ;
	fscanf(in , "%d" , &c) ;
	w = (int*)malloc(sizeof(int)*(n+1)) ;
	bestx = (int*)malloc(sizeof(int)*(n+1)) ;
	for(i =1 ; i<=n ; i++)
	{
		fscanf(in , "%d" , &w[i]) ;
	}

	MaxLoading(w , c , n) ;

	out = fopen("output.txt" , "w") ;
	for(i=1 ; i<=n ; i++)
	{
		fprintf(out , "%d\t" , bestx[i]) ;
	}
	fprintf(out , "\n") ;
	fclose(in);
	fclose(out);
	return 0 ;
}

三.

运行结果:

在input.txt中输入:n=4, c=70, w1=30, w2=25, w3=15, w4=10.

在output.txt中查看运行结果:

这个1 1 1 0是bestx[i]的结果,bestx[i]==1代表选择这个分支,也就是第i个货物装船,为0表示不装船。


你可能感兴趣的:(数据结构与算法分析)