(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.