递归与分治算法简单c语言,基础算法(2)递归与分治算法

(1) (2) (3) (4)

并且每一个方格只能用一层地毯,迷宫的大小为(2k)2的方形。当然,也不能让公主无限制地在那儿等,对吧?由于你使用的是计算机,所以实现时间为1s。

【输入文件】blank.in

输入文件共两行。

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

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

【输出文件】blank.out

将迷宫填补完整的方案:每一行为x y

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

【样例】

Blank.in

3

3 3

Blank.out

5 5 1

2 2 4

1 1 4

1 4 3

4 1 2

4 4 1

2 7 3

1 5 4

1 8 3

3 6 3

4 8 1

7 2 2

5 1 4

6 3 2

8 1 2

8 4 1

7 7 1

6 6 1

5 8 3

8 5 2

8 8 1

解题思路:

看到这个问题后,第一感觉应该是“本题是一个规模为2k的问题”,如果每次都能按坐标将迷宫四等分分成四个规模为2k-1的小迷宫,直到最后变成4×4的最小迷宫,那么应用递归分治算法的思路便有了。但是公主只有一个,另外三个小迷宫没公主了,怎么办?假想3个吧!反正任何一块地毯都能同时覆盖三个格子,只要假想的三个公主能集中在一起,用一块地毯覆盖就行了。

首先对最简单的情况(即k=1)进行分析:公主只会在4个方格中的一个:

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

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

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

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

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

#

#

#

#

#

#

#

#

#

#

#

#

假设公主所在的位置用实心圆表示,即上图中的(1,4),那么我们就可以把3号毯子放在(2,3)处,,这样就将(1,3)至(2,4)的k=1的小迷宫全部覆盖(#表示地毯)。接下来就是3个k=1的情况了,但是有一点不同的是:没有“公主”了,每一个k=1的小迷宫都会留下一个空白(即上图中的空心圆),那么空白就有:1×3=3个,组合后便又是一个地毯形状。

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

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

参考程序:

Program phy;

Var k,x,y,sta,i,ks:integer;

Function ok(x1,y1,x2,y2:integer):Boolean;

Begin

If (x>=x1) and

(x<=x2) and (y>=y1) and

(y<=y2) then exit(true);

Ok:=false;

End;

Function get(x1,y1,x2,y2:integer):Integer;

Begin

If (x<=(x1+x2) div 2) and

(y<=(y1+y2 div 2) then exit(1);

If (x<=(x1+x2) div 2) and

(y>(y1+y2 div 2) then exit(2);

If (x>(x1+x2) div 2) and

(y<=(y1+y2 div 2) then exit(3);

If (x>(x1+x2) div 2) and

(y>(y1+y2 div 2) then exit(4);

End;

Procedure search(x1,y1,x2,y2,s:integer);

Var temp,midx,midy:integer;

Begin

If (x2-x1<=0) or

(y2-y1)<=0) then exit;

Midx:=(x1+x2) div 2;

Midy:=(x1+x2) div 2;

Case s of

1:begin

Writeln(midx+1,’ ’,midy+1,’

’,1);

Temp:=1;

If

ok(x1,y1,midx,midy) then temp:=get(x1,y1,midx,midy);

Search(x1,y1,midx,midy,temp);

Search(x1,midy+1,midx,y2,3);

Search(midx+1,y1,x2,midy,2);

Search(midx+1,midy+1,x2,y2,1);

End;

2:begin

Writeln(midx+1,’ ’,midy,’ ’,2);

Temp:=2;

If

ok(x1,midy+1,midx,y2) then temp:=get(x1,midy+1,midx,y2);

Search(x1,y1,midx,midy,4);

Search(x1,midy+1,midx,y2,temp);

Search(midx+1,y1,x2,midy,2);

Search(midx+1,midy+1,x2,y2,1);

End;

3:begin

Writeln(midx,’ ’,midy+1,’ ’,3);

Temp:=3;

If

ok(midx+1,y1,x2,midy) then temp:=get(midx+1,y1,x2,midy);

Search(x1,y1,midx,midy,4);

Search(x1,midy+1,midx,y2,3);

Search(midx+1,y1,x2,midy,temp);

Search(midx+1,midy+1,x2,y2,1);

End;

4:begin

Writeln(midx,’ ’,midy,’ ’,4);

Temp:=4;

If

ok(midx+1,midy+1,x2,y2) then temp:=get(midx+1,midy+1,x2,y2);

Search(x1,y1,midx,midy,4);

Search(x1,midy+1,midx,y2,3);

Search(midx+1,y1,x2,midy,2);

Search(midx+1,midy+1,x2,y2,temp);

End;

End;

End;

Begin

Readln(k);

Readln(x,y);

Ks:=1;

For i:=1 to k do ks=x*ks;

Sta:=get(1,1,ks,ks);

Search(1,1,ks,ks,sta);

End.

3.纸牌乘法游戏

【问题描述】乘法游戏是在一行牌上进行的。每一张牌包括了一个正整数。在每一次游戏中,玩家拿出一张牌,得分是用它的数字乘以它左边和右边的数,所以不允许拿出第1张和最后一张牌,最后一次游戏后,这里只剩下两张牌。

你的目标是使得分之和最小。

例如,如果数是10 1 50 20 5

,依次拿1,20,50,总分是:10*1*50+50*20*5+10*50*5=8000.而拿50,20,1,总分是:1*50*20+1*20*5+10*1*5=1150.

【输入文件】mul.in

第一行包括牌数n(3≤n≤100),第二行包括n个1~100的整数,用空格隔开。

【输出文件】mul.out

只有一个数字:最小得分

【样例输入】

6

10 1 50 50 20 5

【样例输出】

3650

解题思路:

看完题目,应该很敏感地意识到题目中的n张纸牌这个“n张”就是问题的规模,当n很大时,我们不会计算最小得分之和,但是当n足够小时,比如只有三张纸牌时,游戏的得分是显而易见的、唯一的,就是等于这三张牌中的数字相乘,因此本题继续采用递归分治算法。

当然本题的正解应该是动态规划,但对于刚刚开始学习算法的选手而言,他们并不会动态规划,事实上信息学奥赛训练中应该注重让选手加强“先苦后甜”的体验,本题就先用递归算法,大家只拿到30%的分数,学生会比教师更迫不及待,启发学生思考一下,为什么另外70%的数据会超时呢?其实在这个基础上,引入记忆化搜索、引入以空间换时间,或者干脆引入动态规划,相信学生都能很快理解掌握的。鉴于初学算法,我们偏偏把“好戏留在后头”,现在就老老实实的写递归。

定义一个函数calc(i,j)表示从第i张牌到j张牌的最小游戏得分之和,按照“与规模有关的问题”的处理办法,我们假定最后一次游戏是移动第k张牌,那么calc(i,j)这大问题就自然而然划分成calc(i,k)和

calc(k,j)。至于分出来的两个小问题怎么办?依葫芦画瓢,他们又能分别划分出两个更小的子问题,直到只剩三张牌时就能立即算出得分。程序中还要注意递归的出口。

参考程序(递归):

Const maxn=101;

Var a:array[1..maxn] of integer;

d:array[1..maxn,1..maxn] of longint;

n,t:integer;

function calc(i,j:integer):longint;

var k:integer;temp:longint;

begin

if i=j then begin calc:=0;exit;end;

if i=j-1 then begin calc:=0;eixt;end;

if i=j-2 then begin

calc:=d[i,j];exit;end;

d[I,j]:=maxint;

for k:=i+1 to j-1 do

begin

temp:=calc(I,k)+calc(k,j)+a[i]*a[k]*a[j];

if temp

end;

calc:=d[I,j];

end;

begin

readln(n);

for t:=1 to n do read(a(t));

fillchar(d,sizeof(d),0);

for t:=1 to n-2 do

d[t,t+2]:=a[t]*a[t+1]*a[t+2];

writeln(calc(1,n));

end.

参考程序(动态规划)

Program xx;

Var N: Longint;

i, j, k: Longint;

Num: Array[1..300] Of Longint;

F: Array[0..300,0..300] Of Longint;

Begin

//Assign(input,'Mul.in');reset(input);

//Assign(output,'Mul.out');rewrite(output);

Readln(N);

For i:= 1 To N Do Read(Num[i]);

Fillchar(F,sizeof(F),1);

For i:= 1 To N Do

Begin

F[i, i] := 0;

F[i, i+1] := 0;

End;

For i:= 2 To N-1 Do

F[i-1, i+1] := Num[i-1] * Num[i] * Num[i+1];

For i:= N-2 Downto 1 Do

For j:= i+2 To N Do

For k:= i+1 To j-1 Do

If F[i, k] + Num[i]*Num[k]*Num[j] + F[k, j] < F[i,

j] Then

F[i, j] := F[i, k] + Num[i]*Num[k]*Num[j] + F[k, j];

Writeln(F[1, N]);

//

close(input);

//

close(output);

End.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值