zoukankan      html  css  js  c++  java
  • 大家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.
    
  • 相关阅读:
    LeetCode 258 Add Digits
    LeetCode 231 Power of Two
    LeetCode 28 Implement strStr()
    LeetCode 26 Remove Duplicates from Sorted Array
    LeetCode 21 Merge Two Sorted Lists
    LeetCode 20 Valid Parentheses
    图形处理函数库 ImageTTFBBox
    php一些函数
    func_get_arg(),func_get_args()和func_num_args()的用法
    人生不是故事,人生是世故,摸爬滚打才不会辜负功名尘土
  • 原文地址:https://www.cnblogs.com/htfy/p/3385647.html
Copyright © 2011-2022 走看看