大家AK杯 灰天飞雁NOIP模拟赛题解/数据/标程

数据

http://files.cnblogs.com/htfy/data.zip

简要题解

桌球碰撞

纯模拟,注意一开始就在袋口和v=0的情况。v和坐标可以是小数。为保险起见最好用extended/double类型。

program prob1;

var
ans:array[0..6,0..600] of longint;
n,i,j:longint;
a0,r0,px,py,vx,vy,left,t,newp:extended;
flag:boolean; function dist(x1,y1,x2,y2:extended):extended;inline;begin exit( sqrt(sqr(x1-x2)+sqr(y1-y2))); end;
function store(num:longint;x,y:extended):boolean;inline;
begin
//writeln('x=',x,' y=',y);
//readln;
if x=0 then
if (y>=0) and (y<=r0) then
begin
inc(ans[1,0]);
ans[1,ans[1,0]]:=num;
exit(true);
end else
if (y>=5-r0) and (y<=5) then
begin
inc(ans[4,0]);
ans[4,ans[4,0]]:=num;
exit(true);
end; if x=10 then
if (y>=0) and (y<=r0) then
begin
inc(ans[3,0]);
ans[3,ans[3,0]]:=num;
exit(true);
end else
if (y>=5-r0) and (y<=5) then
begin
inc(ans[6,0]);
ans[6,ans[6,0]]:=num;
exit(true);
end; if y=0 then
if (x>=0) and (x<=r0) then
begin
inc(ans[1,0]);
ans[1,ans[1,0]]:=num;
exit(true);
end else
if (x>=5-r0) and (x<=5+r0) then
begin
inc(ans[2,0]);
ans[2,ans[2,0]]:=num;
exit(true);
end else
if (x>=10-r0) and (x<=10) then
begin
inc(ans[3,0]);
ans[3,ans[3,0]]:=num;
exit(true);
end; if y=5 then
if (x>=0) and (x<=r0) then
begin
inc(ans[4,0]);
ans[4,ans[4,0]]:=num;
exit(true);
end else
if (x>=5-r0) and (x<=5+r0) then
begin
inc(ans[5,0]);
ans[5,ans[5,0]]:=num;
exit(true);
end else
if (x>=10-r0) and (x<=10) then
begin
inc(ans[6,0]);
ans[6,ans[6,0]]:=num;
exit(true);
end;
exit(false);
end; begin readln(n,r0,a0);
fillchar(ans,sizeof(ans),0);
for i:=1 to n do
begin
//writeln('i=',i, ' ');
readln(px,py,vx,vy); left:=(vx*vx+vy*vy)/(2*a0);
if (vx=vy) and (vx=0) then begin {writeln('inti');}store(i,px,py); continue; end;
//process
while left>=0 do
begin
if store(i,px,py) then break;
flag:=false; if vx<0 then
begin
t:=px/(-vx);
newp:=t*vy+py;
if (newp>=0) and (newp<=5) then
begin
flag:=true;
left:=left-dist(px,py,0,newp);
px:=0;
py:=newp;
vx:=-vx;
end;
end else
if vx>0 then
begin
t:=(10-px)/(vx);
newp:=t*vy+py;
if (newp>=0) and (newp<=5) then
begin
flag:=true;
left:=left-dist(px,py,10,newp);
px:=10;
py:=newp;
vx:=-vx;
end;
end; if (vy>0) and not flag then
begin
t:=(5-py)/vy;
newp:=t*vx+px;
if (newp>=0) and (newp<=10) then
begin
flag:=true;
left:=left-dist(px,py,newp,5);
px:=newp;
py:=5;
vy:=-vy;
end;
end else
if vy<0 then
begin
t:=py/(-vy);
newp:=t*vx+px;
if (newp>=0) and (newp<=10) then
begin
flag:=true;
left:=left-dist(px,py,newp,0);
px:=newp;
py:=0;
vy:=-vy;
end;
end; end;
end;
for i:=1 to 6 do
begin
write(ans[i,0]);
if ans[i,0]<>0 then write(' ');
for j:=1 to ans[i,0] do
if j<ans[i,0] then write(ans[i,j],' ') else write(ans[i,j]);
writeln;
end; end.

旋转圆盘

DP,设F[t,i,j]为第t时刻到达第i层第j块的最大得分和。

则F[t,i,j]=w[i,j]+Max{

F[t-1,i,j]//不动

,F[t-1,i,prev(j)]//从前面走过来

,F[t-1,i,next(j)]//从后面走过来

,F[t-1,i-1,j]//从上面跳下来

,F[t-2,i-1,k]+w[i-1,k] (k<>j) //从上面转后跳下来

}

注意到最后一种情况若朴素查找Max{f[t-2,i-1,k] k<>j}则会使转移代价成为O(m),从而整个程序的复杂度会达到O(tn m^2)从而TLE

我们可以记录每一个t,i下F[t,i,k]的最大值、最大值时的maxk 和 除最大值以外的F[t,i,k]max(k<>maxk)

这样我们就可以在O(1)时间内得到Max{f[t-2,i-1,k] k<>j}了

注意到W较大,用int64存储

用滚动数组压缩空间,滚动数组mod 3较慢,不如直接mod 4=and 3快

program prob2;

var
t0,n,m,i,j,t:longint;
ans:int64;
a:array[0..300,0..300] of int64;
f:array[-2..3,-2..300,-2..300] of int64;
max1,max2,pos1,pos2:array[-2..3,-2..300] of int64; function prev(P:int64):int64;inline; begin if p=m then exit(1) else exit(p+1); end;
function next(P:int64):int64;inline; begin if p=1 then exit(m) else exit(p-1); end;
function max(a,b:int64):int64;inline; begin if a>b then exit(a);exit(b); end; begin readln(n,m,t0);
for i:=1 to n do
begin
for j:=1 to m do
read(a[i,j]);
readln;
end; fillchar(f,sizeof(f),$81);
//max1[t0,i] max2 pos1 pos2
fillchar(max1,sizeof(max1),$81);
fillchar(max2,sizeof(max2),$81); f[0,1,1]:=0;
for t:=1 to t0 do
for i:=1 to n do
begin
max1[t and 3,i]:=-maxlongint;max2[t and 3,i]:=-maxlongint;
for j:=1 to m do
begin
f[t and 3,i,j]:=f[(t-1) and 3,i,j];
f[t and 3,i,j]:=max(f[(t-1) and 3,i,j],f[(t-1) and 3,i,prev(j)]);
f[t and 3,i,j]:=max(f[t and 3,i,j],f[(t-1) and 3,i,next(j)]);
f[t and 3,i,j]:=max(f[t and 3,i,j],f[(t-1) and 3,i-1,j]);
if j<>pos1[(t-2) and 3,i-1] then f[t and 3,i,j]:=max(f[t and 3,i,j],max1[(t-2) and 3,i-1]+a[i-1,pos1[(t-2) and 3,i-1]]) else
f[t and 3,i,j]:=max(f[t and 3,i,j],max2[(t-2) and 3,i-1]+a[i-1,pos2[(t-2) and 3,i-1]]); f[t and 3,i,j]:=f[t and 3,i,j]+a[i,j]; if f[t and 3,i,j]>max1[t and 3,i] then begin max1[t and 3,i]:=f[t and 3,i,j];pos1[t and 3,i]:=j; end
else
if f[t and 3,i,j]>max2[t and 3,i] then begin max2[t and 3,i]:=f[t and 3,i,j];pos2[t and 3,i]:=j; end; end;
end; ans:=-maxlongint;
for i:=1 to n do
for j:=1 to m do
ans:=max(ans,f[t0 and 3,i,j]);
writeln(ans); end.

弹幕游戏

直接输出0或1什么的都有30分入账(HT:所以说这是水题模拟赛)

正解是对抗搜索。甲是Max游戏者 乙是Min游戏者

alpha-beta减枝考虑到这是noip模拟赛就没加(其实是因为出不出来卡朴素的数据)(跑了一个上午的对拍器没找到合理的数据)

细节很多,自己看代码吧

program prob3;

Const
TLEFT=1;
TRIGHT=2;
TUP=3;
TDOWN=4;
PLAYERA=false;
PLAYERB=true;
dx:array[1..5] of longint=(-1,1,0,0,0);
dy:array[1..5] of longint=(0,0,1,-1,0);
illegal=$1235;//不合法状态,用一个够大的数标记 Type TBullet=record//子弹定义
x,y,dir:integer;//x y 子弹坐标 dir运动方向,详见前
ava:boolean;//是否要继续处理子弹的运动,如果已经跑到外面了就赋ava=false
end;
TSelfState=record
top,sx,sy,ax,ay,bx,by:longint;//一个游戏者的状态 包含了小怪的位置和自机位置和子弹栈
bullet:array[0..55] of TBullet;
end;
TState=record
pa,pb:TSelfState;//状态 包含了甲乙游戏者的两个网格
score,t:integer;
end; Var
now:TState;
t0:longint;
q:array[0..20,false..true] of longint; Function max(a,b:longint):longint;inline;begin if a>b then exit(a);exit(b); end;
Function min(a,b:longint):longint;inline;begin if a<b then exit(a);exit(b); end; Function missed(var ss:TselfState):boolean;inline;//判断一个网格内是否已经中弹
var
i:longint;
begin
with ss do
begin
if (sx=ax) and (sy=ay) then exit(true);//撞到小怪了
if (sx=bx) and (sy=by) then exit(true);
for i:=1 to top do
if bullet[i].ava and (sx=bullet[i].x) and (sy=bullet[i].y) then exit(true);//撞到子弹了
exit(false);
end;
end; procedure createbullet(var ss:TselfState;px,py,pdir:longint);inline;//在一个游戏者的网格中创建子弹
begin
if not((px>=1) and (px<=5) and (py>=1) and (py<=6)) then exit;//子弹在外面不用创建了
inc(ss.top);
with ss.bullet[ss.top] do
begin
dir:=pdir;
x:=px;
y:=py;
ava:=true;
end;
end; procedure movebullet(var ss:TselfState);inline;//子弹的移动
var
i:longint;
begin
for i:=1 to ss.top do
with ss.bullet[i] do
if ava then
begin
x:=x+dx[dir];
y:=y+dy[dir];
if not((x>=1) and (x<=5) and (y>=1) and (y<=6)) then ava:=false;//子弹到外面就不用处理了
end;
end; procedure createmoz(var ss:TselfState;pt:longint);inline;//创建小怪的子弹
begin
if odd(pt) then //奇数秒 a小怪产生子弹
begin
createbullet(ss,ss.ax+1,ss.ay,TRIGHT);
createbullet(ss,ss.ax-1,ss.ay,TLEFT);
createbullet(ss,ss.ax,ss.ay-1,TDOWN);
end else
begin //偶数秒 b小怪产生子弹
createbullet(ss,ss.bx+1,ss.by,TRIGHT);
createbullet(ss,ss.bx-1,ss.by,TLEFT);
createbullet(ss,ss.bx,ss.by-1,TDOWN);
end; end; function expand(player:boolean;apro:longint):boolean;inline;//apro 操作方式 1左 2右 3上 4下 5ATK 返回表示该操作是否合法 若合法即改写状态
begin if player=PLAYERA then
with now.pa do
begin
if (sx=5) and (apro=2) then exit(false);
if (sx=1) and (apro=1) then exit(false);
if (sy=1) and (apro=4) then exit(false);
if (sy=6) and (apro=3) then exit(false); //在边界还需要向外移动肯定不合法
if missed(now.pa) then dec(now.score); //中弹判定
if apro<5 then begin sx:=sx+dx[apro];sy:=sy+dy[apro]; end else//需要移动的情况
begin //产生子弹的情况
createbullet(now.pb,sx-1,sy+2,TDOWN);
createbullet(now.pb,sx,sy+2,TDOWN);
createbullet(now.pb,sx+1,sy+2,TDOWN);
end;
movebullet(now.pa);//子弹移动
createmoz(now.pa,now.t); //产生小怪子弹
//inc(now.t); a游戏者操作后回合数不会+1~
exit(true);
end; if player=PLAYERB then
with now.pb do
begin
if (sx=5) and (apro=2) then exit(false);
if (sx=1) and (apro=1) then exit(false);
if (sy=1) and (apro=4) then exit(false);
if (sy=6) and (apro=3) then exit(false);
if missed(now.pb) then inc(now.score);
if apro<5 then begin sx:=sx+dx[apro];sy:=sy+dy[apro]; end else
begin
createbullet(now.pa,sx-1,sy+2,TDOWN);
createbullet(now.pa,sx,sy+2,TDOWN);
createbullet(now.pa,sx+1,sy+2,TDOWN);
end;
movebullet(now.pb);
createmoz(now.pb,now.t);
inc(now.t); //b操作完后回合数+1
exit(true);
end; end; function search(dpt:longint;player:boolean):longint;
var
i,o:longint;
oldnow:Tstate;
begin
q[dpt,player]:=illegal;
search:=illegal;
if dpt=t0+1 then
exit(now.score);
oldnow:=now;
for i:=1 to 5 do
begin
now:=oldnow;
if not expand(player,i) then continue;
if player=playera then o:=search(dpt,not player) else o:=search(dpt+1,not player);
if o<>illegal then
if search=illegal then
search:=o
else
if player=playera then //轮到a行动,他会找分差最大的方案
search:=max(search,o)
else
search:=min(search,o);//b行动,分差最小的方案
q[dpt,player]:=search; if player=playera then //alpha-beta剪枝
begin
if (search>q[dpt-1,not player]) and (q[dpt-1,not player]<>illegal) then exit;
end else
if (search<q[dpt,not player]) and (q[dpt,not player]<>illegal) then exit;
end;
end; begin
readln(t0);
filldword(q,sizeof(q) div 4,illegal);
with now.pa do
begin
top:=0;
readln(sx,sy,ax,ay,bx,by);
end;
with now.pb do
begin
top:=0;
readln(sx,sy,ax,ay,bx,by);
end;
now.score:=0;
now.t:=1;
writeln(search(1,playera)); end.
上一篇:winform登录功能


下一篇:UML类图常见关系总结