Stop the Hollyweb! No DRM in HTML5.

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

数据

https://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.

 

posted on 2013-10-24 11:12  灰天飞雁  阅读(631)  评论(0编辑  收藏  举报

填写您的邮件地址,订阅我们的精彩内容:  点击这里给我发消息

添加到收藏夹