#include <stdio.h> #include <string.h> //箱子数目 #define BOXNUM 5 //货船容量 #define MAXLOAD 21 //箱子重量 int box[BOXNUM]= {1,12,9,4,5}; //loadflag[i]为1表示第i个箱子需要装载 int loadflag[BOXNUM]; int bestloadflag[BOXNUM]; //最优装载重量 int bestload=0; //当前装载重量 int curload=0; //初始化数据 void init_data() { bestload=0; curload=0; memset(loadflag,0,sizeof(int)*(BOXNUM)); memset(bestloadflag,0,sizeof(int)*(BOXNUM)); } //递归版本货船装箱。boxi为当前要考察的箱子。 void load_recusive(int boxi) { //如果最后一个箱子已经考察过了,尝试更新最优装载重量。 if(boxi >= BOXNUM) { if(curload>bestload) { bestload=curload; memcpy(bestloadflag,loadflag,sizeof(bestloadflag)); } return; } //尝试装载当前这个箱子,然后递归。 if((curload+box[boxi]) <= (MAXLOAD)) { loadflag[boxi]=1; curload+=box[boxi]; load_recusive(boxi+1); curload-=box[boxi]; loadflag[boxi]=0; } //跳过当前这个箱子,往下递归。 load_recusive(boxi+1); } //货船装箱迭代版本。 //先解释一下搜索解空间树中,移动到左右孩子的意思。 //在解空间树上每条边都标记上0或者1,0-跳过,1-装载。 //我们总是把左孩子标记为1,右孩子标记为0。 //移动到左孩子,就是把箱子装载进来,继续考察后面的箱子。 //移动到右孩子,就是跳过当前这个箱子,继续考察后面的箱子。 //搜索解空间树的策略是: /* 尽量往左孩子移动,直到不能移动为止。 不能移动分为两种情况: (1)移动到了叶子。 (2)当前装载量没有超过货船的容量,但是如果往左孩子移动,就超过了。 如果是第二中情况,那么要尝试移动到右孩子。 只要是移动到了叶子节点,就要回溯。 回溯的策略是: (1)如果是从左孩子回退的,那么移动到右兄弟。 (2)如果是从右孩子回退的,那么先找到最左节点的父节点,然后再移动到右孩子。 如果该算法有描述不清楚的地方,请参考下面的代码。 */ int load_iterate() { //iterative version algorithm. int boxi=0; while(1) { //尽量往左子树走 while((boxi < BOXNUM) && (curload+box[boxi]<=(MAXLOAD))) { //往左子树走,意味着把当前这个box加进来。 curload+=box[boxi]; loadflag[boxi]=1; boxi++; } //往左子树走不下去了,尝试往右子树移动。 if(boxi < BOXNUM) { //往左子树走,意味着跳过当前这个box。继续考虑下一个box. loadflag[boxi]=0; boxi++; } //返回策略。 //如果碰到叶子,就返回。 //如从左孩子返回,就要移动到右兄弟。 //如从右孩子返回,就要回溯到最左节点的父节点的右孩子。 if(boxi>=(BOXNUM)) { //更新最优装载的最大重量。 if(bestload < curload) { bestload=curload; memcpy(bestloadflag,loadflag,sizeof(bestloadflag)); } //从叶子返回 boxi--; if(loadflag[boxi]) { //如果是从左孩子返回,那么要移动到右兄弟。 curload-=box[boxi]; loadflag[boxi]=0; boxi++; } else { //如从右孩子返回,就要回溯到最左节点的右兄弟。 //找到最左节点的父节点。 while(boxi>=0 && !loadflag[boxi]) { boxi--; } if(boxi<0) { printf("/n%s,%d,%s,end/n",__FILE__,__LINE__,__FUNCTION__); return bestload; } //移动到右孩子。 curload-=box[boxi]; loadflag[boxi]=0; boxi++; } } } printf("/n%s,%d,%s,end/n",__FILE__,__LINE__,__FUNCTION__); } int main() { int i; init_data(); int result=load_iterate(); printf("/n%s,%d,%s,loadnum:%d/n",__FILE__,__LINE__,__FUNCTION__,result); for(i=0; i<BOXNUM; i++) { printf(" %d ",bestloadflag[i]); } return 0; } 无论是递归版本还是迭代版本,解空间树的节点数目都是2^n, 因此时间复杂度就是O(2^n)。 改天再补充一些图例说明。