• [POJ1082&POJ2348&POJ1067&POJ2505&POJ1960]简单博弈题总结


      鉴于时间紧张...虽然知道博弈是个大课题但是花一个上午时间已经极限了...

      希望省选过后再回过头来好好总结一遍吧。

      接下来为了看着顺眼一点...还是按照难度顺序吧


     

    POJ1082

      一道最简单的博弈题,只需要DP就可以过。

      在这道题里我尽情展示了多函数多过程的代码风格。。

    program poj1082;
    const u:set of 0..20=[1,3,5,7,8,10,12];
    var n,i,x,y,z:longint;
        f:array[1900..2002,1..12,1..31]of boolean;
    
    function leap(x:longint):boolean;
    begin
        if x mod 100=0 then
        begin
            if x mod 400=0 then exit(true);
        end else
        begin
            if x mod 4=0 then exit(true);
        end;
        exit(false);
    end;
    
    function exist(x,y,z:longint):boolean;
    begin
        if y in u then
            begin
                    if z<32 then exit(true);
            end else
        begin
            if y<>2 then
            begin
                if z>30 then exit(false) else exit(true);
            end else
            begin
                if (z<29)or(leap(x)and(z=29)) then exit(true);
            end;
        end;
        exit(false);
    end;
    
    function next_day(x,y,z:longint):boolean;
    begin
        inc(z);
        if not exist(x,y,z) then
        begin
            inc(y);z:=1;
        end;
        if y>12 then
        begin
            inc(x);y:=1;
        end;
        exit(f[x,y,z]);
    end;
    
    function next_month(x,y,z:longint):boolean;
    begin
        inc(y);
        if y>12 then
        begin
            inc(x);y:=1;
        end;
        if not exist(x,y,z) then exit(false);
        exit(f[x,y,z]);
    end;
    
    procedure solve;
    var i,j,k:longint;
        flag:boolean;
    begin
        fillchar(f,sizeof(f),false);
        for i:=2001 downto 1900 do
            for j:=12 downto 1 do
                for k:=31 downto 1 do if exist(i,j,k) then
                begin
                    flag:=true;
                    if next_day(i,j,k) then flag:=false;
                    if next_month(i,j,k) then flag:=false;
                    f[i,j,k]:=flag;
                end;
    end;
    
    begin
        assign(input,'poj1082.in');reset(input);
        readln(n);
        solve;
            for i:=1 to n do
        begin
            readln(x,y,z);
            if f[x,y,z] then writeln('NO') else writeln('YES');
        end;
    end.

    POJ2960

      简单的SG函数的运用。

    program poj2960;
    const maxn=10010;maxm=110;
    var sizes,i,n,m,x,ans,j:longint;
        s:array[-1..maxm]of longint;
        w:array[-1..maxn]of longint;
        vis:array[-1..2*maxn,-1..maxm]of boolean;
    
    procedure calc_SG;
    var i,j:longint;
    begin
        fillchar(vis,sizeof(vis),false);
        for i:=0 to maxn do
        begin
            for j:=0 to maxm do if not vis[i,j] then break;
            w[i]:=j;
            for j:=1 to sizes do vis[i+s[j],w[i]]:=true;
        end;
    end;
    
    begin
        //assign(input,'poj2960.in');reset(input);
        //assign(output,'poj2960.out');rewrite(output);
        read(sizes);
        while sizes<>0 do
        begin
            for i:=1 to sizes do read(s[i]);readln;
            calc_SG;
            readln(m);
            for i:=1 to m do
            begin
                ans:=0;
                read(n);
                for j:=1 to n do
                begin
                    read(x);
                    ans:=ans xor w[x];
                end;
                            if ans>0 then write('W') else write('L');
                readln;
            end;
                    writeln;
            read(sizes);
        end;
    end.

    POJ2505

      题面很亲切,就是不停乘上2~9之间的一个数,超过了某个数n即算赢。

      依稀记得去年暑假做过这道题,当时应该数据比较弱是直接DP过的...

      然后先还是无脑DP,然后输出发现了很神奇的规律...

      2~9 先手赢

      10~18 后手赢

      19~162 先手赢

      163~324 后手赢

      ...

      想来其实也并无道理,但是找规律无疑是最快捷的方法了。

    program poj2505;
    var x:int64;
    
    function solve(x:int64):boolean;
    var k:int64;
    begin
        k:=1;
        while true do 
        begin
            k:=k*9;
            if x<=k then exit(true);
            k:=k*2;
            if x<=k then exit(false);
        end;
    end;
    
    begin
        while not eof do 
        begin
            readln(x);
            if solve(x) then writeln('Stan wins.') else writeln('Ollie wins.');
        end;
    end.

    POJ2348

      这道题我的思考出现了一点问题...

      刚开始认为,对于每一个(x,y)到(y,x mod y)的过程,都可以看做是一场Nim游戏

      然后石子的个数就是x div y的个数

      自己觉得非常有道理,然后就SG敲起来了...

      发现怎么都过不去,和标算对拍了之后发现是这样的...

      Nim游戏你可以任意选一堆石子开始,而这道题是从大减到小

      也就是强制必须先从第一堆石子里取完才能从第二堆取

      于是就不可以用SG函数了

      继续看,发现如果当x div y=1的时候,只有一种选择

      而当x div y>=2的时候,起码有两种选择而且显然是可以交换和对手的身份的

      这种情况下就是必胜的。

      然后辗转相除套一个特判就可以AC啦

    program poj2348;
    var tem,x,y:int64;
    
    function solve(x,y:int64):boolean;
    begin
        if y=0 then exit(false);
        if x div y>=2 then exit(true);
        exit(not solve(y,x mod y));
    end;
    
    begin
        assign(input,'poj2348.in');reset(input);
        readln(x,y);
        while (x<>0)or(y<>0) do
        begin
            if x<y then
            begin
                tem:=x;x:=y;y:=tem;
            end;
            if solve(x,y) then writeln('Stan wins')
            else writeln('Ollie wins');
                    readln(x,y);
        end;
    end.

    POJ1067 

      一道裸的威佐夫博弈...

      两堆石子中可以在任意一堆中取若干个,也可以在两堆中取相同数量个。

      奇异状态(x,y)满足:

        x=a[i],y=a[i]+i

      而a[i]=i*(sqrt(5)-1)/2+i;

      非奇异状态为必胜状态,反之必败。

      我是用二分来实现x=a[i]这一步的查找的...

      (刚开始非常sx的用了1.618后来发现x<=10^9,乘到后面显然精度不够,然后copy来了一堆长的黄金分割数,但是忘记把小数点前改1了...然后一直WA。发现其实就是(sqrt(5)-1)/2...既简单不用背有精确度高...真是初三内容都还给李老师了...连黄金分割都忘掉了QAQ)

    program poj1067;
    const INF=1000000000;
          num=(sqrt(5)-1)/2+1;
    var x,y,tem:int64;
    
    function find(x:int64):int64;
    var L,R,mid,tem:int64;
    begin
        L:=0;R:=INF;
        while L<=R do
        begin
            mid:=(L+R) >> 1;
            tem:=trunc(num*mid);
            if x=tem then exit(mid);
            if x<tem then R:=mid-1 else L:=mid+1;
        end;
        exit(-1);
    end;
    
    begin
        while not eof do
        begin
            readln(x,y);
            if x>y then
            begin
                tem:=x;x:=y;y:=tem;
            end;
            tem:=find(x);
            if (tem=-1)or(tem+x<>y) then writeln(1) else writeln(0);
        end;
    end.
            
  • 相关阅读:
    二叉搜索树
    自己课题的任务
    查找
    排序
    python快速教程
    塌下心来,慢慢积累~
    生命只有一次,你可以用它来做一点伟大的事情
    Python 编程规范-----转载
    Some regret....
    Discussing the scenery in the program of 863 with Doctor Zhang!
  • 原文地址:https://www.cnblogs.com/mjy0724/p/4375466.html
Copyright © 2020-2023  润新知