F[i][j]=max{|a[i]-a[j]|*(j-i+1),f[i][i]+f[i+1][j],F[i][i+1]+F[i+2][j],...,F[i][j-1]+F[j][j]}
F[1,n]为所求的解
时间复杂度:
o(N^3);
此题需要选手对算法的时间复杂度进行分析,简单的搜索是不能在规定时间内求出结果,必需使用高效的算法,主要考查选手运用高效算法——动态规划解决问题的能力。
源程序:
var
n:
longint;
a:
array[1..100]oflongint;
f:
array[0..101,0..101]oflongint;
procedureinit;
vari:
longint;
begin
readln(n);
fori:
=1tondoread(a[i]);
end;
functionwork(i,k:
longint):
longint;
begin
ifi=kthenexit(a[i]);
exit(abs(a[i]-a[k])*(k-i+1));
end;
functionmax(a,b:
longint):
longint;
begin
ifa>bthenmax:
=aelsemax:
=b;
end;
proceduremain;
vari,j,p:
longint;
begin
fillchar(f,sizeof(f),0);
fori:
=1tondof[i,i]:
=a[i];
fori:
=ndownto1do
forj:
=i+1tondo
begin
forp:
=i+1toj+1do
f[i,j]:
=max(f[i,j],f[p,j]+work(i,p-1));
forp:
=j-1downtoi-1do
f[i,j]:
=max(f[i,j],f[i,p]+work(p+1,j));
end;
end;
procedureprint;
begin
writeln(f[1,n]);
end;
begin
assign(input,'remove.in');reset(input);
assign(output,'remove.out');rewrite(output);
init;
main;
print;
close(input);close(output);
end.
3.俄罗斯方块
(game.pas/c/cpp)
【问题描述】
相信大家都玩过“俄罗斯方块”游戏吧,“俄罗斯方块”是一个有趣的电脑小游戏,现有一个有C列、行不受限定游戏平台,每一次下落的方块是下列的7个图形的一种:
在下落的过程中,游戏者可以作90、180或270度旋转,还可以左右移动,对于每一次方块落地,我们要求方块的每一部分都必须与地面(最底面或己落下的方块上表面)接触,例如,有一个宽度为6列的平台,每一列的初始高度(已经占用的方格数)分别为2,1,1,1,0和1。
编号为5的方块下落,有且仅有5种不同的落地方法:
现给出每一列的初始高度和下落方块的形状,请你编写一个程序,求出落地的方法总数,也就是落地后,地表面形成的不同的形状总数。
输入:
第一行为二个整数C和P,1≤C≤100,1≤P≤7,表示列数和下落方块的编号
第二行共有用一个空隔隔开的C个整数,每一个数字在0到100,之间(包含0和100),表示每一列的初始高度
输出:
输出为一个整数,表示落地的方法总数
样例数据
Input1
65
211101
Output1
5
Input2
51
00000
Output2
7
Input3
94
435465766
Output3
1
分析:
我们用编码序列表示下落方块的底部,比如编号为5的方块经过4种旋转得到4个序列:
{1,1,1},{1,2},{2,1,2}和{2,1}。
如果序列A上每一个元素都加上同一个常数得到序列B,我们就称序列A和序列B是对应的,比如序列{2,1,2}与{5,4,5}对应,但不与{4,5,4}或{5,4}对应。
可以看出,当且仅当方块的序列与地面相对位置的高度形成的序列对应,那么这个方块能与地面能结合。
因此对于给定的方块和地面,我们只要简单地检查所有旋转所形成的可能的形状,这样我们就可以用枚举法求解。
此题比较简单,用枚举法解决,在实现中要考虑各种变换。
主要考查选手的基本的程序实现能力。
源程序:
programgame;
constMAXS=100;
fin='game.in';
fout='game.out';
var
v:
array[1..MAXS]oflongint;
s,f,i,count:
longint;
begin
assign(input,fin);reset(input);
assign(output,fout);rewrite(output);
readln(s,f);
if(f=4)or(f=7)then{如果是第四种和第七种,则变成第三种和第六种}
begin
dec(f);
fori:
=1tosdoread(v[s-i+1]);
endelse
fori:
=1tosdoread(v[i]);
count:
=0;
casefof
1:
begin
count:
=s;
fori:
=1tos-3do
if(v[i]=v[i+1])and(v[i]=v[i+2])and(v[i]=v[i+3])theninc(count);
end;
2:
begin
fori:
=1tos-1do
ifv[i]=v[i+1]theninc(count);
end;
3:
begin
fori:
=1tos-1do
ifv[i]=v[i+1]+1theninc(count);
fori:
=1tos-2do
if(v[i]=v[i+1])and(v[i]=v[i+2]-1)theninc(count);
end;
5:
begin
fori:
=1tos-2do
if((v[i]-v[i+1]=1)or(v[i]-v[i+1]=0))and(v[i]=v[i+2])theninc(count);
fori:
=1tos-1do
ifabs(v[i]-v[i+1])=1theninc(count);
end;
6:
begin
fori:
=1tos-2do
if(v[i+1]=v[i+2])and((v[i+1]-v[i]=1)or(v[i+1]-v[i]=0))theninc(count);
fori:
=1tos-1do
if(v[i]=v[i+1])or(v[i]=v[i+1]+2)theninc(count);
end;
end;
writeln(count);
close(input);
close(output);
end.
4.燃烧木棍
(fire.pas/c/cpp)
【问题描述】
Tom是调皮的孩子,特别喜欢玩火,现在他手上有若干根长度分别为1和
的木棍,还有一张不能燃烧的平板,他把平板划分成长度为1的单元格,并标上座标。
然后按以下规则把平板上的木棍组成一个连通图:
木棍的两端必须放在单元格的顶点上。
即长度为1的木棍放在单元格的某一边上,长度为
的木棍放在单元格的对角线上。
Tom的点火规则是,只能从木棍的两端点火,而不能从木棍的中间点火。
如图,不能在A点点火,但在C点或B点点火都是充许的。
点火后,火会沿着木棍向前燃烧(每根都有自己的燃烧速度),并能点燃与它相接的其它木棍。
任务:
写一程序计算从哪里开始点火,使得燃烧的总时间最短,输出最短时间。
InputData
输入文件bete.in第一行为一个正整数N,表示组成图形的木棍数目,后面共有N行,每行5个数:
X1Y1X2Y2T,其中(X1,Y1)和(X2,Y2)分别表示木棍两端的坐标,T表示木棍燃烧时间,是指从木棍的某一端点火燃烧到别一端,燃完所需的时间。
OutputData
输出文件bete.out是一个保留4位小数的实数,表示所有木棍完全燃烧的最少时间。
约束1≤n≤40
保证图形是连通的,所有的木棍长度都是1或
,没有任何两根木棍重合.
-200≤X1,Y1,X2,Y2≤200;0≤T≤107.
如果你的输出结果与标准答案相差小于0.001,则认为你的结果正确。
样例1
fire.in
fire.out
解释
1
00111
1.0000
从任一端点火都行,燃烧时间都是1
样例2
fire.in
fire.out
解释
5
00011
100110
00101
00111
22111
3.2500
在(0,0)位置点火
木棍1,3和4将被点燃,燃烧0.5分钟后,木棍2将被从中间点燃向两端燃烧,再过0.5分钟,木棍1,3,4将被完全燃烧,木棍5将被点燃并在1分钟后燃烧完(比木棍2早燃完)。
木棍2从中间向两端燃烧0.5分钟以后,变成两小段,每段的燃烧时间是4.5分钟。
但因为此时两小段木棍的另一端也同时被点燃,燃烧速度变成原来的两倍,还需2.25分钟的燃烧时间,所以总时间:
1+2.25=3.25
样例3
fire.in
fire.out
解释
3
111210
122210
112250
35.0000
在(1,2)位置点火,木棍(11,12)和(12,22)将燃烧10分钟。
.最后一根木棍在10分钟后从两端被点燃,燃烧时间为25分钟。
分析:
在此题问题中,木棍构成的是一个连通图,如果我们直接构图处理比较复杂,我们对原问题进行转换,由于木棍与木棍之间只能在木棍的两端或中间相交。
我们把每根木棍拆分成两根相等的小木棍,这样,木棍的数量增加了一倍,原问题就转化为,木棍与木棍之间只能在木棍的两端相交,这样处理起来就比较方便。
我们以木棍为边,木棍与木棍之间的交点为顶点,构建一个连通图,问题变为寻找一个合适的顶点,使得点燃以后完全燃烧的时间最短。
很显然,燃烧时间等于点燃的顶点到图中最远点的时间,如下图
我们可以先求出某个点到其它所有点的最短时间,在这里我们可以使用Floyd’s算法求出任意两点之间的最短时间,时间复杂度为O(N3)。
注意,我们在这里用Floyd’s算法求的是第个点被点燃的时间,每个点被点燃,并不代表所有木棍都被完全燃烧,如上图。
求出从某个点到其它点的最短时间以后,余下的问题是检查每一边是否完全燃烧。
如果没有完全燃烧,求出剩余边燃烧所需最长时间。
对于燃烧时间为L的木棍,它的两端被点燃的时刻为T1和T2,如果T1=T2+L或者是T2=T1+L,那么燃烧到T1和T2的最大时刻,这根木棍己经完全燃烧。
如果T1与T2之间的时间差不等于L,那么就说明火是从不同的路径燃烧到这根木棍的两端。
火将从两端向中间燃烧,并在木棍内的某个点燃完,在简单情况中,如果是从两端同时点燃,燃烧时间为L/2。
更一般地,如果T1与T2不等,我们设一端是从0时刻点燃,另一端是从T时刻点燃,那么这根木棍的燃烧时间为T+(L-T)/2.
此题比较复杂,先要构图,并且要对原图形进行转换,再用Floyd’s算法求出任意两点之间的最少时间,最后还要检查所有的边是否燃烧,此题考查选手用图解决问题的能力,并且此题不能简单的套用Floyd’s算法,需要选手在解决问题中灵活运用经典算法。
源程序:
ProgramMatches;
Const
MaxN=42;{N根木棍的端点}
MaxG=2*MaxN+1;{拆分以后的最大顶点数}
Infinity=MaxLongInt;{燃烧时间的最大值}
VarN:
Integer;
Match:
Array[1..MaxN]OfRecord{每根木棍构成边的信息:
端点座标和燃烧时间}
X1,Y1,X2,Y2:
Integer;
Time:
LongInt;
End;
NG:
Integer;
Vertex:
Array[1..MaxG]OfRecord
X,Y:
Integer;{图的顶点信息}
End;
Edge,Distance:
Array[1..MaxG,1..MaxG]OfLongInt;
Res:
Extended;{燃烧最少时间}
ResX,ResY:
Integer;
ProcedureInit;{输入}
VarI:
Integer;
Begin
Read(N);
ForI:
=1ToNDo
WithMatch[I]Do
Read(X1,Y1,X2,Y2,Time);
End;
FunctionGetVertex(VX,VY:
Integer):
Integer;
{intoarcenumarulvarfuluicucoordonateledatedacalipseste,estecreat}
VarI:
Integer;
Begin
ForI:
=1ToNGDo
WithVertex[I]Do
If(X=VX)And(Y=VY)ThenBegin
GetVertex:
=I;
Exit;
End;
Inc(NG);{Dacavarfulnuestegasit}
WithVertex[NG]DoBegin
X:
=VX;
Y:
=VY;
ForI:
=1ToNG-1DoBegin
Edge[I,NG]:
=Infinity;
Edge[NG,I]:
=Infinity;
End;
Edge[NG,NG]:
=0;
End;
GetVertex:
=NG;
End;
ProcedureAddEdge(X1,Y1,X2,Y2:
Integer;Time:
Longint);
{Functiaadaugamuchieintrevarfuri}
VarA,B:
Integer;
Begin
A:
=GetVertex(X1,Y1);
B:
=GetVertex(X2,Y2);
Edge[A,B]:
=Time;
Edge[B,A]:
=Time;
End;
ProcedureBuildGraph;{构图}
VarI:
Integer;
Begin
NG:
=0;
ForI:
=1ToNDo
WithMatch[I]DoBegin
AddEdge(X1*2,Y1*2,X1+X2,Y1+Y2,Time);
AddEdge(X1+X2,Y1+Y2,X2*2,Y2*2,Time);
End;
End;
ProcedureFindShortestPaths;{Floyd’s算法求任意两点之间的最短时间}
VarK,I,J:
Integer;
Begin
Distance:
=Edge;
ForK:
=1ToNGDo
ForI:
=1ToNGDoIfDistance[I,K]ForJ:
=1ToNGDoIfDistance[K,J]IfDistance[I,K]+Distance[K,J]Distance[I,J]:
=Distance[I,K]+Distance[K,J];
End;
FunctionBurnAt(At:
Integer):
Extended;
VarI,J:
Integer;
Curent,ThisEdge:
Extended;
Begin
Curent:
=0;
ForI:
=1ToNGDoIfDistance[At,I]>CurentThenCurent:
=Distance[At,I];
ForI:
=1ToNGDo
ForJ:
=I+1ToNGDoIfEdge[I,J]If(Distance[At,I](Distance[At,J]IfDistance[At,I]ThisEdge:
=Distance[At,J]+(Edge[I,J]