• 大家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.
    
  • 相关阅读:
    如何使用ERStudio 生成comment
    windows 使用excel导出的问题
    iOS Runtime(一)、objc_class深深的误解
    iOS开发安全
    iOS微信运动 刷分
    APP中的 H5和原生页面如何分辨、何时使用
    Weex系列二、显示图片
    Android系列一、创建项目
    Weex系列一、构建Weex工程
    时间复杂度
  • 原文地址:https://www.cnblogs.com/htfy/p/3385647.html
Copyright © 2020-2023  润新知