就简单的背包问题举例:
设有一个背包可以放入的物品重量为S,现有n件物品,重量分别为w1,w2,.. .,wn。问能否从这n件物品中选择若干件放入此背包,使得放入的重量之和正好为S。如果存在一种符合上述要求的选择,则称此背包问题有解,否则此问题无解,试用递归方法设计此背包问题的算法。
用递归思想解决问题,要学会把大问题化为小问题,把看似一个整体的问题细化到其中的一部,进行思考。并且要学会用“撒手”观念去解决问题。
例如背包问题的解决,首先拿到问题思考,把这个大的问题化为单个物品的小问题,则对于单个物品来说就有两种情况,一是装进背包,二是不装进背包,这样就好考虑了。
如图所示,递归的出口是当S=0,或者S<0,或者S>0且n<1;
int knapsack(int S,int n)
{ if(S==0) return 1;
else if(S<0||(S>0&&n<1)) return 0;
else
{ if(knapsack(S-W[n],n-1))
{ cout<<W[n]; return 1; }
return(knapsack(S,n-1));
}
}
这是代码,只不过N是从最大值开始的,需要不断的减。
其中的:
if(knapsack(S-W[n],n-1))
{ cout<<W[n]; return 1; }
return(knapsack(S,n-1));
则是对递归的实现,
if(knapsack(S-W[n],n-1))
{ cout<<W[n]; return 1; }
如果当前的物品放进去了,会出来一个结果,如果不成功,则不放进去,进行下一次的选择。此时就用到了“撒手”的观念,我不管以后的操作,knapsack(S-W[n],n-1)就是能够判断当装进这一个物品是否会成功装满。
如果最后不成功则此时就会选择不装进去。
续更:
看完了我的有关递归的文章,是否对一个新的递归可以自己独立的分析出来,或者自己拿到一个题,可以自己设计出一个递归来。
快速幂的递归实现:
LL S=1;
LL di_pow(LL a,LL b){
if(b==0) return 1;
S=di_pow(a*a,b>>1);
if(b&1) S*=a;
return S;
}
这段代码是否能够看懂?
在函数体中,函数的实参的作用域和作用时间需要我们好好的研究一下。
在此递归函数中,S=di_pow(a*a,b>>1); 这是子问题的生成段。
生成的子问题是:
从上到下分析这个问题,最开始是最大的次方,从这开始,开始转换为比它小的且满足b的对应二进制位的运算。比如:3的22次方。 22次方拆分成 16 4 2;转换成二进制是10110.当b右移一位时,a*a; 因为是传参函数,所以作用域是整个函数体,而这是递归,有其独特性。