高级本分治应用2.docx

上传人:b****5 文档编号:6751179 上传时间:2023-01-10 格式:DOCX 页数:18 大小:65.21KB
下载 相关 举报
高级本分治应用2.docx_第1页
第1页 / 共18页
高级本分治应用2.docx_第2页
第2页 / 共18页
高级本分治应用2.docx_第3页
第3页 / 共18页
高级本分治应用2.docx_第4页
第4页 / 共18页
高级本分治应用2.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

高级本分治应用2.docx

《高级本分治应用2.docx》由会员分享,可在线阅读,更多相关《高级本分治应用2.docx(18页珍藏版)》请在冰豆网上搜索。

高级本分治应用2.docx

高级本分治应用2

第四章分治应用

分治习题集

4.1取余运算

源程序名mod.?

?

?

(pas,c,cpp)

可执行文件名mod.exe

输入文件名mod.in

输出文件名mod.out

【问题描述】

输入b,p,k的值,求bpmodk的值。

其中b,p,k*k为长整型数。

【样例】

mod.inmod.out

21092^10mod9=7

【知识准备】

进制转换的思想、二分法。

【算法分析】

本题主要的难点在于数据规模很大(b,p都是长整型数),对于bp显然不能死算,那样的话时间复杂度和编程复杂度都很大。

下面先介绍一个原理:

a*bmodk=(amodk)*(bmodk)modk。

显然有了这个原理,就可以把较大的幂分解成较小的,因而免去高精度计算等复杂过程。

那么怎样分解最有效呢?

显然对于任何一个自然数P,有p=2*pdiv2+pmod2,如19=2*19div2十19mod2=2*9+1,利用上述原理就可以把b的19次方除以k的余数转换为求b的9次方除以k的余数,即b19=b2*9+1=b*b9*b9,再进一步分解下去就不难求得整个问题的解。

这是一个典型的分治问题,具体实现的时候是用递推的方法来处理的,如p=19,有19=2*9+1,9=2*4+1,4=2*2+0,2=2*1+0,1=2*0+1,反过来,我们可以从0出发,通过乘以2再加上一个0或1而推出1,2,4,9,19,这样就逐步得到了原来的指数,进而递推出以b为底,依次以这些数为指数的自然数除以k的余数。

不难看出这里每一次乘以2后要加的数就是19对应的二进制数的各位数字,即1,0,0,1,1,而19=(10011)2,求解的过程也就是将二进制数还原为十进制数的过程。

具体实现请看下面的程序,程序中用数组binary存放p对应的二进制数,总位数为len,binary[1]存放最底位。

变量rest记录每一步求得的余数。

4.2地毯填补问题

源程序名blank.?

?

?

(pas,c,cpp)

可执行文件名blank.exe

输入文件名blank.in

输出文件名blank.out

【问题描述】

相传在一个古老的阿拉伯国家里,有一座宫殿。

宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:

公主就站在其中一个方格子上,只要谁能用地毯将除公主站立的地方外的所有地方盖上,美丽漂亮聪慧的公主就是他的人了。

公主这一个方格不能用地毯盖住,毯子的形状有所规定,只能有四种选择(如图4-l):

(1)

(2)(3)(4)

并且每一方格只能用一层地毯,迷宫的大小为(2k)2的方形。

当然,也不能让公主无限制的在那儿等,对吧?

由于你使用的是计算机,所以实现时间为1s。

【输入】

输入文件共2行。

第一行:

k,即给定被填补迷宫的大小为2k(0

第二行:

xy,即给出公主所在方格的坐标(x为行坐标,y为列坐标),x和y之间有一个空格隔开。

【输出】

将迷宫填补完整的方案:

每一补(行)为xyc(x,y为毯子拐角的行坐标和列坐标,c为使用毯子的形状,具体见上面的图1,毯子形状分别用1、2、3、4表示,x、y、c之间用一个空格隔开)。

【样例】

blank.inblank.out

3551

33224

114

143

412

441

273

154

183

363

481

722

514

632

812

841

771

661

583

852

881

【知识准备】

分治思想和递归程序设计。

【算法分析】

拿到这个问题后,便有一种递归重复的感觉。

首先对最简单的情况(即k=1)进行分析:

公主只会在4个方格中的一个:

左上角:

则使用3号毯子补,毯子拐角坐标位于(2,2);{下面就简称为毯子坐标}

左下角:

则使用2号毯子补,毯子拐角坐标位于(1,2);

右上角:

则使用1号毯子补,毯子拐角坐标位于(2,1);

右下角:

则使用4号毯子补,毯子拐角坐标位于(1,1);

其实这样不能说明什么问题,但是继续讨论就会有收获,即讨论k=2的情况(如图4-1):

#

#

#

#

#

#

#

#

#

#

#

#

我们假设公主所在的位置用实心圆表示,即上图中的(1,4),那么我们就可以把1号毯子放在(2,3)处,这样就将(1,3)至(2,4)的k=1见方全部覆盖(#表示地毯)。

接下来就是3个k=1的见方继续填满,这样问题就归结为k=1的情况了,但是有一点不同的是:

没有“公主”了,每一个k=1的小见方都会留下一个空白(即上图中的空心圆),那么空白就有:

1*3=3个,组合后便又是一个地毯形状。

好了,现在有感觉了吧,我们用分治法来解决它!

对于任意k>1的宫殿,均可以将其化分为4个k/2大小的宫殿,先看一下公主站的位置是属于哪一块,因为根据公主所在的位置,我们可以确定中间位置所放的毯子类型,再递归处理公主所站的那一块,直到出现边界条件k=1的情况,然后在公主边上铺上一块合适的地毯,递归结束。

由于要递归到每一格,复杂度就是面积,就是O(22*k*k)。

4.3平面上的最接近点对

源程序名nearest.?

?

?

(pas,c,cpp)

可执行文件名nearest.exe

输入文件名nearest.in

输出文件名nearest.out

【问题描述】

给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的。

【输入】

第一行:

n;2≤n≤60000

接下来n行:

每行两个实数:

xy,表示一个点的行坐标和列坐标,中间用一个空格隔开。

【输出】

仅一行,一个实数,表示最短距离,精确到小数点后面4位。

【样例】

nearest.innearest.out

31.0000

11

12

22

【参考程序】

中位数、解析几何、时间复杂度、二分法

【算法分析】

如果n很小,那么本题很容易。

我们只要将每一点对于其它n-1个点的距离算出,找出最小距离即可。

时间复杂度O(n2)。

但本题n很大,显然会超时。

所以我们要寻找更快的解决问题的方法。

我们首先想到能不能缩小计算的规模,即把n个点的问题分治成一些小规模的问题呢?

由于二维情况下的问题计算过于复杂,所以先讨论一维的情况,假设点集为S。

我们用X轴上某个点m将点集S划分为2个子集S1和S2,使得S1={x∈S|x≤m};S2={x∈S|x>m}。

这样一来,对于所有p∈S1和q∈S2有p

递归地在S1和S2上找出其最接近的点对{p1,p2}和{q1,q2},并设δ=MIN{|p2-p1|,|q2-q1|},S中的最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{p3,q3},其中p3∈S1且q3∈S2。

如图4-2所示:

图4-2一维情形的分治法

我们注意到,如果S的最接近点对是{p3,q3},即|p3-q3|<δ,则p3和q3两者与m的距离不超过δ,即|p3-m|<δ,|q3-m|<δ,也就是说,p3∈(m-δ,m),q3∈(m,m+δ)。

由于每个长度为δ的半闭区间至多包含S1中的一个点,并且m是S1和S2的分割点,因此(m-δ,m)中至多包含S中的一个点,则此点就是S1中的最大点。

同理,如果(m-δ,m)中有S中的点,则此点就是S2中最小点。

因此,我们用线性时间就可以将S1的解和S2的解合并成为S的解。

也就是说,按这种分治策略,合并可在O(n)时间内完成。

这样是否就可以得到一个有效算法了呢?

还有一个问题有待解决,即分割点m的选择,即S1和S2的划分。

选取分割点m的一个基本要求是由此导出集合S的一个线性分割,即S=S1∪S2,S1≠φ,S2≠φ,且S1∈{x|x≤m},S2∈{x|x>m}。

容易看出,如果选取m=(MAX(S)+MIN(S))/2,可以满足线性分割的要求。

选取分割点后,再用O(n)时间即可将S划分成S1={x∈S|x≤m}和S2={x∈S|x>m}。

然而,这样选取分割点m,有可能造成划分出的子集S1和S2的不平衡。

例如在最坏情况下,S1只有1个,S2有n-1个,由此产生的分治在最坏情况下所需的时间T(n)应满足递归方程:

T(n)=T(n-1)+O(n)

它的解是T(n)=O(n2)。

这种效率降低的现象可以通过分治中“平衡子问题”的方法加以解决。

也就是说,我们可以通过适当选择分割点m,使S1和S2中有大致相等个数的点。

自然地,我们会想到用S的n个点的坐标的中位数来作分割点。

这样一来,我们就能在O(n)的时间里确定m(证明略),从而得到效率相对较高的分割点。

至此,我们可以设计出一个求一维点集S中最接近点对的距离的算法:

Functionnpair1(S);

Begin

IfS中只有2个点

Thenδ:

=|x[2]-x[1]|

ElseifS中只有1个点Thenδ:

=∞

Elsebegin

M:

=S中各点的坐标值的中位数;

构造S1和S2;{S1∈{x|x≤m},S2∈{x|x>m}}

δ1:

=npair1(S1);δ2:

=npair1(S2);

P:

=max(S1);Q:

=min(S2);

δ:

=min(δ1,δ2,q-p);

end;

Exit(δ)

End;

由以上的分析可知,该算法的分割步骤总共耗时O(n)。

因此,算法耗费的计算时间T(n)满足递归方程:

T

(2)=1

T(n)=2T(n/2)+O(n);

解此递归方程可得T(n)=O(n*Log2n)。

这个一维问题的算法看上去比用排序加扫描更加复杂,然而它可以推广到二维。

假设S为平面上的点集,每个点都有2个坐标值x和y。

为了将点集S线性分割为大小大致相等的2个子集S1和S2,我们选取一垂直线l:

x=m来作为分割直线。

其中m为S中各点X坐标的中位数。

由此将S分割为S1={p∈S|x(p)≤m}和S2={p∈S|x(p)>m}。

从而使S1和S2分别位于直线l的左侧和右侧,且S=S1∪S2。

由于m是S中各点X坐标值的中位数,因此S1和S2中的点数大致相等。

递归地在S1和S2上求解最接近点对问题,我们分别得到S1和S2中的最小距离δ1和δ2。

现设δ=min(δ1,δ2)。

若S的最接近点对(p,q)之间的距离d(p,q)<δ,则p和q必分属于S1和S2。

不妨设p∈S1,q∈S2。

那么,p和q距直线L的距离均小于δ。

因此,我们若用P1和P2分别表示直线L的左边和右边的宽为δ的2个垂直长条,则p∈P1且q∈P2,如图4-3所示:

据直线L的距离小于δ的所有点

在一维的情形下,距分割点距离为δ的2个区间(m-δ,m),(m,m+δ)中最多各有S中一个点,因而这2点成为惟一的未检查过的最接近点对候选者。

二维的情形则要复杂一些,此时,P1中所有点与P2中所有点构成的点对均为最接近点对的候选者。

在最坏情况下有

对这样的候选者。

但是P1和P2中的点具有以下的稀疏性质,它使我们不必检查所有这

对候选者。

考虑P1中任意一点p,它若与P2中的点q构成最接近点对的候选者,则必有d(p,q)<δ。

满足这个条件的P2中的点有多少个呢?

容易看出这样的点一定落在一个δ*2δ的矩形R中(如图4-4所示)。

由δ的意义可知,P2中任何2个S中的点的距离都不小于δ。

由此而来可以推出矩形R中最多只有6个δ/2*2/3*δ的矩形(如图4-5所示)。

图4-4包含点q的δ*2δ矩形R图4-5图4-6

若矩形R中有多于6个S中的点,则由鸽笼原理易知至少有一个δ/2*2/3*δ的小矩形中有2个以上S中的点。

设U,V是这样2个点,它们位于同一小矩形中,则:

因此,

这与δ的意义相矛盾。

也就是说矩形R中最多只有6个S中的点。

图4-6是矩形R中含有S中的6个点的极端情形。

由于这种稀疏性质,对于P1中任一点p,P2中最多只有6个点与它构成最接近点对的候选者。

因此,在分治的合并步骤中,我们最多需要检查

对候选者,而不是

对候选者。

这是否就意味着我们可以在O(n)时间内完成分治法的合并步骤呢?

现在还不能确定,因为我们还不知道要检查哪6个点。

为了解决这个问题,我们可以将p和P2中所有S2的点投影到垂线L上。

由于能与p点一起构成最接近点对候选者的S2中点一定在矩形R中,所以它们在直线L上的投影距p在L上投影点的距离小于δ。

由上面的分析可知,这种投影点最多只有6个。

因此,若将P1和P2中所有S的点按其Y坐标排好序,则对P1中所有点,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者,对P1中每一点最多只要检查P2中排好序的相继6个点。

至此,我们得出用分治法求二维最接近点对距离的算法:

Functionnpair(s);

Begin

IfS中只有2个点

Thenδ:

=S中这2点的距离

ElseifS中只有1个点

Thenδ:

=∞

Elsebegin

(1)m:

=S中各点X坐标值的中位数;

构造S1和S2;

(2)δ1:

=npair1(S1);

δ2:

=npair1(S2);

(3)δm:

=min(δ1,δ2);

(4)设P1是S1中距垂直分割线L的距离在δm之间的所有点组成的集合,

P2是S2中距分割线L的距离在δm之间的所有点组成的集合。

将P1

和P2中的点依其Y坐标值从小到大排序,并设P1*和P2*是相应的已

排好序的点列;

(5)通过扫描P1*,对于P1*中每个点检查P2*中与其距离在δm之内的所

有点(最多6个)可以完成合并。

当P1*中的扫描指针可在宽为2*δm

的一个区间内移动。

设δ1是按这种扫描方式找到的点对间的最小距

离;

(6)δ:

=min(δm,δt);

end;

Exit(δ)

End;

下面我们来分析上述算法的时间复杂性。

设对于n个点的平面点集S,在

(1)和(5)步用了O(n)时间,(3)和(6)用的是常数时间,

(2)则用了

时间,而在(4),最坏情况要O(nlog2n)时间,仍然无法承受,所以我们在整个程序的开始时,就先将S中的点对按Y座标值排序,这样一来(4)和(5)两步的时间就只需O(n)的时间了,所以总的计算时间同样满足:

T

(2)=1,

由此,该问题的时间复杂度为O(nlog2n),在渐进的意义下为最优算法了。

空间复杂度为O(n)。

4.4求方程的根

源程序名equation.?

?

?

(pas,c,cpp)

可执行文件名equation.exe

输入文件名equation.in

输出文件名equation.out

【问题描述】

输入m,n,p,a,b,求方程f(x)=mx+nx-px=0在[a,b]内的根。

m,n,p,a,b均为整数,且a

如果有根,则输出,精确到1E-11;如果无方程根,则输出“NO”。

【样例】

equation.inequation.out

234121.5071265916E+00

2.9103830457E-11

【算法分析】

首先这是一个单调递增函数,对于一个单调递增(或递减)函数,如图4-7所示,判断在[a,b]范围内是否有解,解是多少。

方法有多种,常用的一种方法叫“迭代法”,也就是“二分法”。

先判断f(a)·f(b)≤0,如果满足则说明在[a,b]范围内有解,否则无解。

如果有解再判断x=(a+b)/2是不是解,如果是则输出解结束程序,否则我们采用二分法,将范围缩小到[a,x)或(x,b],究竟在哪一半区间里有解,则要看是f(a)·f(x)<0还是f(x)·f(b)<0。

当然对于yx,我们需要用换底公式把它换成exp(xln(y))。

4.5小车问题

源程序名car.?

?

?

(pas,c,cpp)

可执行文件名car.exe

输入文件名car.in

输出文件名car.out

【问题描述】

甲、乙两人同时从A地出发要尽快同时赶到B地。

出发时A地有一辆小车,可是这辆小车除了驾驶员外只能带一人。

已知甲、乙两人的步行速度一样,且小于车的速度。

问:

怎样利用小车才能使两人尽快同时到达。

【输入】

仅一行,三个数据分别表示AB两地的距离s,人的步行速度a,车的速度b。

【输出】

两人同时到达B地需要的最短时间。

【样例】

car.incar.out

1205259.6000000000E+00

【算法分析】

最佳方案为:

甲先乘车到达K处后下车步行,小车再回头接已走到C处的乙,在D处相遇后,乙再乘车赶往B,最后甲、乙一起到达B地。

这样问题就转换成了求K处的位置,我们用二分法,不断尝试,直到满足同时到达的时间精度。

算法框架如下:

(1)输入s,a,b;

(2)c0:

=0;c1:

=s;c:

=(c0+c1)/2;

(3)求t1,t2;

(4)如果t1

=(c0+c)/2

否则c:

=(c+c1)/2;

反复执行(3)和(4),直到abs(t1-t2)满足精度要求(即小于误差标准)。

4.6黑白棋子的移动

源程序名chessman.?

?

?

(pas,c,cpp)

可执行文件名chessman.exe

输入文件名chessman.in

输出文件名chessman.out

【问题描述】

有2n个棋子(n≥4)排成一行,开始为位置白子全部在左边,黑子全部在右边,如下图为n=5的情况:

○○○○○●●●●●

移动棋子的规则是:

每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。

每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。

如n=5时,成为:

○●○●○●○●○●

任务:

编程打印出移动过程。

【样例】

chessman.inchessman.out

7step0:

ooooooo*******--

step1:

oooooo--******o*

step2:

oooooo--******o*

step3:

ooooo--*****o*o*

step4:

ooooo*****--o*o*

step5:

oooo--****o*o*o*

step6:

oooo****--o*o*o*

step7:

ooo--***o*o*o*o*

step8:

ooo*o**--*o*o*o*

step9:

o--*o**oo*o*o*o*

step10:

o*o*o*--o*o*o*o*

step11:

--o*o*o*o*o*o*o*

【问题分析】

我们先从n=4开始试试看,初始时:

○○○○●●●●

第1步:

○○○——●●●○●(—表示空位)

第2步:

○○○●○●●——●

第3步:

○——●○●●○○●

第4步:

○●○●○●——○●

第5步:

——○●○●○●○●

如果n=5呢?

我们继续尝试,希望看出一些规律,初始时:

○○○○○●●●●●

第1步:

○○○○——●●●●○●

第2步:

○○○○●●●●——○●

这样,n=5的问题又分解成了n=4的情况,下面只要再做一下n=4的5个步骤就行了。

同理,n=6的情况又可以分解成n=5的情况,……,所以,对于一个规模为n的问题,我们很容易地就把它分治成了规模为n-1的相同类型子问题。

数据结构如下:

数组c[1..max]用来作为棋子移动的场所,初始时,c[1]~c[n]存放白子(用字符o表示),c[n+1]~c[2n]存放黑子(用字符*表示),c[2n+1],c[2n+2]为空位置(用字符—表示)。

最后结果在c[3]~c[2n+2]中。

4.7麦森数(NOIP2003)

源程序名mason.?

?

?

(pas,c,cpp)

可执行文件名mason.exe

输入文件名mason.in

输出文件名mason.out

【问题描述】

形如2p-1的素数称为麦森数,这时P一定也是个素数。

但反过来不一定,即如果P是个素数,2p-1不一定也是素数。

到1998年底,人们已找到了37个麦森数。

最大的一个是P=3021377,它有909526位。

麦森数有许多重要应用,它与完全数密切相关。

任务:

从文件中输入P(1000

【输入】

文件中只包含一个整数P(1000

【输出】

第一行:

十进制高精度数2p-1的位数;

第2~11行:

十进制高精度数2p-1的最后500位数字(每行输出50位,共输出10行,不足500位时高位补0);

不必验证2p-1与P是否为素数。

【样例】

mason.in

1279

mason.out

386

00000000000000000000000000000000000000000000000000

00000000000000000000000000000000000000000000000000

00000000000000104079321946643990819252403273640855

38615262247266704805319112350403608059673360298012

23944173232418484242161395428100779138356624832346

49081399066056773207629241295093892203457731833496

61583550472959420547689811211693677147548478866962

50138443826029173234888531116082853841658502825560

46662248318909188018470682222031405210266984354887

32958028878050869736186900714720710555703168729087

【问题分析】

本题的解题方法很多,其中分治也是一种巧妙的方法。

首先可以想到:

2n=(2ndiv2)2*2nmod2,即:

如果要计算2n,就要先算出2ndiv2,然后用高精度乘法算它的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1