以下是引用片段:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{/*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{将物品i包含在当前方案中;
if(i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if(不包含物品i仅是可男考虑的)
if(i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
以下是引用片段:
#include
#defineN100
doublelimitW,totV,maxV;
intoption[N],cop[N];
struct{doubleweight;
doublevalue;
}a[N];
intn;
voidfind(inti,doubletw,doubletv)
{intk;
/*考虑物品i包含在当前方案中的可能性*/
if(tw+a.weight<=limitW)
{cop=1;
if(i
else
{for(k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if(tv-a.value>maxV)
if(i
else
{for(k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
voidmain()
{intk;
doublew,v;
printf(“输入物品种数
”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值
”);
for(totv=0.0,k=0;k
{scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量
”);
scanf(“%1f”,&limitV);
maxv=0.0;
for(k=0;kfind(0,0.0,totV);
for(k=0;k
if(option[k])printf(“%4d”,k+1);
printf(“
总价值为%.2f
”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
以下是引用片段:
#include
#defineN100
doublelimitW;
intcop[N];
structele{doubleweight;
doublevalue;
}a[N];
intk,n;
struct{int;
doubletw;
doubletv;
}twv[N];
voidnext(inti,doubletw,doubletv)
{twv.=1;
twv.tw=tw;
twv.tv=tv;
}
doublefind(structele*a,intn)
{inti,k,f;
doublemaxv,tw,tv,totv;
maxv=0;
for(totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While(i>=0)
{f=twv.;
tw=twv.tw;
tv=twv.tv;
switch(f)
{case1:twv.++;
if(tw+a.weight<=limitW)
if(i
{next(i+1,tw+a.weight,tv);
i++;
}
else
{maxv=tv;
for(k=0;k
cop[k]=twv[k].!=0;
}
break;
case0:i--;
break;
default:twv.=0;
if(tv-a.value>maxv)
if(i
{next(i+1,tw,tv-a.value);
i++;
}
else
{maxv=tv-a.value;
for(k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
returnmaxv;
}
voidmain()
{doublemaxv;
printf(“输入物品种数
”);
scanf((“%d”,&n);
printf(“输入限制重量
”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值
”);
for(k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“
选中的物品为
”);
for(k=0;k
if(option[k])printf(“%4d”,k+1);
printf(“
总价值为%.2f
”,maxv);
}
递归的基本概念和特点
程序调用自身的编程技巧称为递归( recursion)。
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
(1) 递归就是在过程或函数里调用自身;
2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。