MCS 简单博弈

今天题解重点写思路!!

博弈参考博客:

http://blog.csdn.net/shahdza/article/details/7796775

Problem A:

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1079 (HDU 1079 )

思路:

目标是 11.4。可以推出11.3和11.1一定会胜利
在向前推可以得出10.30 10.28 10.26 10.24…..(我把10月份的都写出来了)都是必胜的。
那么大致可以得出月份+日期为偶数为必胜状态。
如果是7月24呢( Adam输 )?首先他要经过
步骤:7月24→8月24→9月24→10月24。
玩家:Adam→Eve→Adam→Eve
此时为Eve必胜。
但是:9 月30和11月30是两个例外(自己可以推一推)
题目中有说道闰年(没卵用)。2月29。可以转换成3月1,或者3月29。但是因为Adam先手。他可以选择一种。
但是3月1和3月29的必胜性和是否闰年没关系。此时都是EVE必胜
所以可以得出结论:
只需判断月份+日期的奇偶性和两个特例就可以了

代码如下:

#include 
#include 
#include 

using namespace std;

int main()
{
    int T;
    scanf("%d",&T);
    while( T-- )
    {
        int y,m,d;
        scanf("%d %d %d",&y,&m,&d);
        if( ( m + d ) % 2 == 0 || d == 30 && m == 9  || d == 30 && m == 11 )
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

Problem B :

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1525( HDU 1525 )

思路:

这题是标准的威佐夫博弈问题。
但是我用的并不是模板方法。一下为kuangbin大神的代码。我只是看懂了
先交换A和B的值。保证A>=B
1.如果A可以整除B。那么Stan可以直接把Ollie弄死
理由: 只要将A中拿走A-(B+1)个。就可以使得剩下B+1和B个。这样无论后手怎么拿都是胜利的。如果Ollie不按套路出牌强行剩下B-1和B个。那么Stan可以剩下B-1和B-2个。只需保证一堆比另一堆多1个就可以了。到了最后剩1和2的时候。无论Ollie有什么套路。他都输了

2.同理 a/b >= 2也是一个意思。可以判断怎么拿可以进入奇异状态。

3.最麻烦的就是不是以上两种情况。那么每次都取余(这样已经不会有什么特殊情况了。只有等到其中一个变成0)。注意:这种情况每次要更改胜利的人。不然会WA

代码如下:

#include 
#include 
#include 

using namespace std;

int main()
{
    int a,b;
    while( ~scanf("%d %d",&a,&b) )
    {
        if( a == 0 && b == 0 ) break;
        int win = 0;
        if( a < b )
            swap(a,b);
        while( b )
        {
            if( a % b == 0 || a / b >= 2 )
                break;
            a = a - b;
            swap( a,b);
            win ^= 1 ;
        }
        if( win == 0  )
            printf("Stan wins\n");
        else
            printf("Ollie wins\n");
    }
    return 0;
}

Problem C:

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1564 ( HDU 1564 )

思路:

首先分两种情况:
1.走完n*n个的格子。那么需要注意此时要走n*n-1步。说明了n为奇数是走偶数步。n为偶数时走奇数步。所以n为偶数时,先走的胜利(因为最后一步是他走的)

2.不按套路出牌。围追堵截(在没有走完全部格子的情况下就结束了)
此时可以得到一样的结论。
因为不会插入图片。示例不好说明。可以自己去试一试。

代码如下:

#include 
#include 
#include 

using namespace std;



int main()
{
    int N;
    while( ~scanf("%d",&N) )
    {
        if( N == 0 )
            break;
        if( N % 2 == 0 )
            printf("8600\n");
        else
            printf("ailyanlu\n");
    }
    return 0;
}

Problem D:

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1864 (HDU 1864)

思路:

标准的巴什博奕。
每次都给对手留下m+1或者他的整数倍。这样如果他去了K个。你都可以取完或者取m+1-K个。这样无论他怎么取。你最后都可以直接收。所以在开始的时候直接判断n和m+1之间的关系就可以了。如果n刚好为m+1的整数倍。那么你就输了。

注意:
这里所说的1不是一个定值。这个值只表示在该游戏中所取得最小值。底下有另外一道题可以说明

代码如下:

#include 
#include 
#include 
#include 
using namespace std;



int main()
{
    int N;
    scanf("%d",&N);
    while( N-- )
    {
        int n,m;
        scanf("%d %d",&n,&m);
        if( n % ( m + 1 ) != 0  )
            printf("first\n");
        else
            printf("second\n");
    }
    return 0;
}

Problem E:

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2147 (HDU 2147)

思路:

从右上角到左下角。总步数为奇数时。先走的人可以获胜。
最短路(只算直走) = n + m -2(起点和转角点)
1.两条边都是偶数。
可以。因为这样可以倒数第二行或者第二列的时候向斜下方走。这样可以把两步改成一步。使偶数步变成奇数步可以达成目的。因为是在倒数行变道。对手已经没有机会变道(原本直线前行。突然向斜下方走一步)了。所以kiki可以赢

2.一条为奇数。一条为偶数
可以。本身就可以直接达成目的。

3.两条边都是奇数。
不行。此时n + m - 2 比为偶数。而且在倒数第二行或第二列的时候是ZZ走。就算你在前面一步强行变道。你认为根据最优方案她不会变回来吗?

注意:
斜着走要的步数其实就是n和m中较小的那个值

代码如下:

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int n,m;
    while( ~scanf("%d %d",&n,&m) )
    {
        if( n == 0 && m == 0 )
            break;
        if(  ( n % 2 == 1 &&  m % 2 == 0 ) || ( n % 2 == 0 && m % 2 == 1) || ( n % 2 == 0 && m %2 ==0 ) )
            printf("Wonderful!\n");
        else
            printf("What a pity!\n");
    }
    return 0;
}

Problem F:

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2897 ( HDU 2897 )

思路:

这题也是巴什博奕。
与前面那题不同之处在于。这题每次最少要取P个最多Q个。剩下P个要一次全收
所以 之前的n对(m +1 )取余改成 n对(p + q )取余就可以了。但是要注意。取余所剩下的值一定要不大于p。如果大于p。根据这题的规则你就输了。

代码如下:

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int n,p,q;
    while( ~scanf("%d %d %d",&n,&p,&q) )
    {
        if( n % ( p + q ) != 0 && n % ( p + q ) <= p )
            printf("LOST\n");
        else
            printf("WIN\n");
    }
    return 0;
}

Problem G:

原题链接:

http://poj.org/problem?id=1067 (POJ 1067)

思路:

这道题用威佐夫博奕
首先:保证a是较小的值
然后威佐夫博奕的公式:
aK = 是前面未出现的最小数。
bk = aK + K;
所以这里用b - a 就可以求得K。
接着利用公式就可以得出答案。
(那个公式是怎么来的。可以百度百科威佐夫博奕。里面有证明)
依据公式:
把计算得到的值向下取整和a值做比较就可以判断当前是不是奇异态
在这题中。一开始为奇异态就输了。

代码如下:

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int a,b;
    while(~scanf("%d %d",&a,&b))
    {
        if ( a > b )
            swap( a, b );
        int k = b - a;
        double j = ( 1 + sqrt( (double)5 ) ) / (double)2;
        if( a == floor( k* j ) )
            printf("0\n");
        else
            printf("1\n");
    }
    return 0;
}

Problem H:

本体我也不会做。第三种算法看不懂。请看大神题解。链接如下:
http://m.blog.csdn.net/blog/u013634213/38835695

你可能感兴趣的:(专题题解)