C++经典算法.docx
- 文档编号:3819157
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:41
- 大小:43.42KB
C++经典算法.docx
《C++经典算法.docx》由会员分享,可在线阅读,更多相关《C++经典算法.docx(41页珍藏版)》请在冰豆网上搜索。
C++经典算法
分治算法的基本思想
是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问,性质相同。
求出子问题的解,就可得到原问题的解。
分治法解题的一般步骤:
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
例1、比赛安排(noip1996)
设有2^n(n<=6)个球队进行单循环比赛,计划在2^n-1天内完成,每个队每天进行一场比赛。
设计一个比赛的安排,使在2^n-1天内每个队都与不同的对手比赛。
例如n=2时的比赛安排为:
队1234
比赛1-23-4第一天
1-32-4第二天
1-42-3第三天
算法分析:
此题很难直接给出结果,我们先将问题进行分解,设m=2^n,将规模减半,如果n=3(即m=8),8个球队的比赛,减半后变成4个球队的比赛(m=4),4个球队的比赛的安排方式还不是很明显,再减半到两个球队的比赛(m=2),两个球队的比赛安排方式很简单,只要让两个球队直接进行一场比赛即可:
1
2
2
1
分析两个球队的比赛的情况不难发现,这是一个对称的方阵,我们把这个方阵分成4部分(即左上,右上,左下,右下),右上部分可由左上部分加1(即加m/2)得到,而右上与左下部分、左上与右下部分别相等。
因此我们也可以把这个方阵看作是由M=1的方阵所成生的,同理可得M=4的方阵:
1
2
3
4
2
1
4
3
3
4
1
2
4
3
2
1
同理可由M=4方阵生成M=8的方阵:
1
2
3
4
5
6
7
8
2
1
4
3
6
5
8
7
3
4
1
2
7
8
5
6
4
3
2
1
8
7
6
5
5
6
7
8
1
2
3
4
6
5
8
7
2
1
4
3
7
8
5
6
3
4
1
2
8
7
6
5
4
3
2
1
这样就构成了整个比赛的安排表。
在算法设计中,用数组a记录2^n个球队的循环比赛表,整个循环比赛表从最初的1*1方阵按上述规则生成2*2的方阵,再生成4*4的方阵,……直到生成出整个循环比赛表为止。
变量h表示当前方阵的大小,也就是要生成的下一个方阵的一半。
源程序:
var
i,j,h,m,n:
integer;
a:
array[1..32,1..32]ofinteger;
begin
readln(n);
m:
=1;a[1,1]:
=1;h:
=1;
fori:
=1tondom:
=m*2;
repeat
fori:
=1tohdo
forj:
=1tohdobegin
a[i,j+h]:
=a[i,j]+h;{构造右上角方阵}
a[i+h,j]:
=a[i,j+h];{构造左下角方阵}
a[i+h,j+h]:
=a[i,j];{构造右下角方阵}
end;
h:
=h*2;
untilh=m;
fori:
=1tomdo
begin
forj:
=1tomdowrite(a[i,j]:
4);
writeln;
end;
end.
在分治算法中,若将原问题分解成两个较小的子问题,我们称之为二分法,由于二分法划分简单,所以使用非常广泛。
使用二分法与使用枚举法求解问题相比较,时间复杂度由O(N)降到O(log2N),在很多实际问题中,我们可以通过使用二分法,达到提高算法效率的目的,如下面的例子。
例2一元三次方程求解(noip2001tg)
题目大意:
给出一个一元三次方程f(x)=ax3+bx2+cx+d=0,求它的三个根。
所有的根都在区间[-100,100]中,并保证方程有三个实根,且它们之间的差不小于1。
算法分析:
在讲解枚举法时,我们讨论了如何用枚举法求解此题,但如果求解的精度进一步提高,使用枚举法就无能为力了,在此我们再一次讨论如何用二分法求解此题。
由题意知(i,i+1)中若有根,则只有一个根,我们枚举根的值域中的每一个整数x(-100≤x≤100),设定搜索区间[x1,x2],其中x1=x,x2=x+1。
若:
⑴f(x1)=0,则确定x1为f(x)的根;
⑵f(x1)*f(x2)<0,则确定根x在区间[x1,x2]内。
⑶f(x1)*f(x2)>0,则确定根x不在区间[x1,x2]内,设定[x2,x2+1]为下一个搜索区间;
若确定根x在区间[x1,x2]内,采用二分法,将区间[x1,x2]分成左右两个子区间:
左子区间[x1,x]和右子区间[x,x2](其中x=(x1+x2)/2)。
如果f(x1)*f(x)≤0,则确定根在左区间[x1,x]内,将x设为该区间的右界值(x2=x),继续对左区间进行对分;否则确定根在右区间[x,x2]内,将x设为该区间的左界值(x1=x),继续对右区间进行对分;
上述对分过程一直进行到区间的间距满足精度要求为止(即x2-x1<0.005)。
此时确定x1为f(x)的根。
源程序:
{$N+}
var
x:
integer;
a,b,c,d,x1,x2,xx:
extended;
functionf(x:
extended):
extended;
begin
f:
=((a*x+b)*x+c)*x+d;
end;
begin
read(a,b,c,d);
forx:
=-100to100do
begin
x1:
=x;x2:
=x+1;
iff(x1)=0thenwrite(x1:
0:
2,'')
elseiff(x1)*f(x2)<0then
begin
whilex2-x1>=0.005do
begin
xx:
=(x1+x2)/2;
iff(x1)*f(xx)<=0thenx2:
=xx
elsex1:
=xx;
end;{while}
write(x1:
0:
2,'');
end;{then}
end;{for}
end.
回溯法算法
如果上期的“百钱买百鸡”中鸡的种类数是变化的,用枚举法就无能为力了,这里介绍另一种算法——回溯法。
回溯法是一种既带有系统性又带有跳跃性的搜索法,它的基本思想是:
在搜索过程中,当探索到某一步时,发现原先的选择达不到目标,就退回到上一步重新选择。
它主要用来解决一些要经过许多步骤才能完成的,而每个步骤都有若干种可能的分支,为了完成这一过程,需要遵守某些规则,但这些规则又无法用数学公式来描述的一类问题。
下面通过实例来了解回溯法的思想及其在计算机上实现的基本方法。
例1、从N个自然数(1,2,…,n)中选出r个数的所有组合。
算法分析:
设这r个数为a1,a2,…ar,把它们从大到小排列,则满足:
(1)a1>a2>…>ar;
(2)其中第i位数(1<=i<=r)满足ai>r-i;
我们按以上原则先确定第一个数,再逐位生成所有的r个数,如果当前数符合要求,则添加下一个数;否则返回到上一个数,改变上一个数的值再判断是否符合要求,如果符合要求,则继续添加下一个数,否则返回到上一个数,改变上一个数的值……按此规则不断循环搜索,直到找出r个数的组合,这种求解方法就是回溯法。
如果按以上方法生成了第i位数ai,下一步的的处理为:
(1)若ai>r-i且i=r,则输出这r个数并改变ai的值:
ai=ai-1;
(2)若ai>r-i且i≠r,则继续生成下一位ai+1=ai-1;
(3)若ai<=r-i,则回溯到上一位,改变上一位数的值:
ai-1=ai-1-1;
算法实现步骤:
第一步:
输入n,r的值,并初始化;i:
=1;a[1]:
=n;
第二步:
若a[1]>r-1则重复:
若a[i]>r-i,①若i=r,则输出解,并且a[i]:
=a[i]-1;
②若i<>r,则继续生成下一位:
a[i+1]:
=a[i]-1;i:
=i+1;
若a[i]<=r-i,则回溯:
i:
=i-1;a[i]:
=a[i]-1;
第三步:
结束;
程序实现
varn,r,i,j:
integer;
a:
array[1..10]ofinteger;
begin
readln(n,r);i:
=1;a[1]:
=n;
repeat
ifa[i]>r-ithen{符合条件}
ifi=rthen{输出}
begin
forj:
=1tordowrite(a[j]:
3);
writeln;
a[i]:
=a[i]-1;
end
else{继续搜索}
begina[i+1]:
=a[i]-1;i:
=i+1;end
else{回溯}
begini:
=i-1;a[i]:
=a[i]-1;end;
untila[1]=r-1;
end.
下面我们再通过另一个例子看看回溯在信息学奥赛中的应用。
例2数的划分(noip2001tg)
问题描述整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:
n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。
输入:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 经典 算法
![提示](https://static.bdocx.com/images/bang_tan.gif)