递归 第20页
[例1]划分问题
设s是一个具有n个元素的集合s
下列条件的子集合sl,s z,·。,s k:
1.si 56呼
(al,a z,·。,a。),现将s集合划分成K个满足
2.S;门Sj=69 ’
3.S1廖S 2LJ S 3LJ·.·廖Sn=S ·
(1毒i,j毒k,i,6j)
则称s n,s z,…,s n是s的一个划分。它相当于把s集合中的n个元素a n·.·a n放人k
个无标号的盒子中,使得没有一个盒子为空。试确定n个元素a1…an放人k个无标号
盒的划分数s(n,k)。
分析:
设n个元素a n…a n故人k个无标号盒的划分数为s(n,k)。在配置过程中,有两种互
不相容的情况:
1.设(a n)是k个子集中的一个子集,于是把(a n…a n—1)划分为k一1子集有s(n一
1,k—1)个划分数;
2.如果(an)不是k个子集中的一个,即an必与其它的元素构成一个子集。首先把
(a,,…,an—1)划分成k个子集,这共有s(n一1,k)种划分方式。然后再把an加入到k
个子集中的一个子集中去,这有k种加入方式。对于每一种加入方式,都使集合划分为k
个子集,因此由乘法原理知,划分数共有
k*s(n一1,k)。
应用加法原理于上述两种情况,得出I a,,…,an)划分为k个子集的划分数:
s(n,k)=s(n一1,k一1)十k*s(n一1,k) (n>1,k>=1)
现在考虑s(n,k)的边界条件:1). s(n,0)=0; 当k>n时s(n,k)=0;
2).s(n,1)=1; s(n,n)=1;
由此得到递归定义:
s(n,k)=s(n一1,k一1)十k*s(n一1,k) (n>k,k>=1)
s(n,k)=o (n<k)或(k=0<n)
s(n,k)=1 (k=1)或(k=n)
题解:
#include<iostream.h>
int s(int n,int k)
{
int sum;
if(n<k||k==0) sum=0;
else if(k==1||k==n) sum=1;
else sum=s(n-1,k-1)+k*s(n-1,k);
return sum;
}
int main()
{
int n,k;
while(cin>>n>>k){
int sum=s(n,k);
cout<<sum<<endl;
}
return 1;
}
分治法 第22页
所谓分治策略:既将原问题分成n个规模较小而结构与原问题相似的
子问题。递归地解这些子问题,然后合并其结果就得到原问题的解。n=2
时的分治法又称二分法。
分治法在每一层递归上都有三个步骤:
分解:将原问题分解成一系列子问题;
解决:递归地解各子问题。若子问题足够小,则直接解
合并:将子问题的结果合并成原问题的解;
[例1I合并排序
对序列A[1],A[2],……,A[n]进行合并排序。
算法分析:
合并排序的算法就是二分法。
分解:将n个元素各含rn/2。1(或Ln/2J)个元素的子序列
解决:用合并排序法对两个子序列递归地排序;
合并:合并两个已排序的子序列以得到排序结果。
前面在贪心法中用到的函数 merge(int p,int q,int r)与merge(int p,int r)就
是典型的二分排序法。这里不再重复。
枚举法 第31页
所谓枚举法,指的是从可能的解的集合中一一枚举各元素, 用题目给定的检验条件
判定哪些是无用的,哪些是有用的。能使命题成立,即为其解.
一般来说,如果预先对问题确定了解的个数以及各个解的值域。我们则可以利用ror语
句和条件判断语句逐步求解或证明.
如果我们无法预先确定解的个数或各解的值域,我们只能采用隐式图搜索的算法求
解,例如回溯法等。由于回溯搜索,每个可能解的枚举一般不止一次,因此在相同的检验
条件下,回溯法要比枚学法费时一些。
[例0填写运算符
输入任意5个数 x1,x2,x3,x4,x5
每相邻两个数间填上一个运算符。在填入四个运算符后,使得表达式值为一个指定值
y(y由键盘输入)。求出所有满足条件的表达式。
分析:为了解决运算的优先级问题,我们设置如下变量:
f——减法标志。减法运算时,置f=一1,否则f=1;
q——若当前运算符为十(一)时,q存贮运算符的左项值;
若当前运算符为*(/)时,q存贮两数乘(除)后的结果;
p——累加器。每填一个算符p=p十f *q。
然后四重for循环,解决问题。这是一道典型的枚举题,运算
量很大。具体程序还是比较简单,故不再转化。
枚举法有其计算量大的缺点,但是如果能够排除那些明显不属
于解集的元素,在局部地方使用枚举法,其效果会十分显著。
[例2]时针问题
在图1.5。1所示的3×3阵列中有9个时钟,我们的目标是旋转时钟指针,使所有
时钟的指针都指向12点。允许旋转时钟指针的方法有9种,每一种移动用一个数字号(1,
2,…,9)表示。 图1.5—2示出了9个数字号与相应的受控制的时钟,这些时钟在图中以灰
色标出,其指针将顺时针旋转90度。
输入数据:
输入9个数码,这些数码给出9个时钟针的初始位置。数码与时刻
的对应关系为0-->12点,1->3点,2-->6点,3-->9点。
例:3 3 0 2 2 2 2 1 2
输出数据:
输出一个最短的移动序列(数字)
例:5 8 4 9
题解:
#include<iostream.h>
int clocks[9],map[9];
void init()
{
int i;
for(i=0;i<9;i++){
cin>>clocks[i];
clocks[i]=(4-clocks[i])%4;
}
}
int order(int k)
{
int c=k;
while(c>4)c-=4;
while(c<0)c+=4;
return c;
}
void clock()
{
init();
int i; bool flag=false;
for(map[0]=0;map[0]<=3;map[0]++)
for(map[1]=0;map[1]<=3;map[1]++)
for(map[2]=0;map[2]<=3;map[2]++){
map[3]=order(clocks[0]-map[0]-map[1]);
map[5]=order(clocks[2]-map[1]-map[2]);
map[4]=order(clocks[1]-map[0]-map[1]-map[2]);
map[6]=order(clocks[3]-map[0]-map[3]-map[4]);
map[8]=order(clocks[5]-map[2]-map[4]-map[5]);
map[7]=order(clocks[7]-map[6]-map[8]-map[4]);
if(clocks[6]==(map[3]+map[6]+map[7])%4&&
clocks[4]==(map[4]+map[0]+map[2]+map[6]+map[8])%4&&
clocks[8]==(map[5]+map[7]+map[8])%4){
for(i=0;i<9;i++)
if(map[i]!=0)cout<<(i+1);
cout<<endl;
flag=true;
}
}
if(!flag)cout<<"NO ANSWER!";
}
int main()
{
clock();
return 1;
}