博弈小结:HDU1907 HDU 1760 HDU1079

最近学了SG值,就感觉博弈题目大多都可以解决了。然后学长说还有一个东西:能到达必败点的点必胜。所有的后续点都是必胜点 这个点必败。并且给我推荐了HDU的1760,

说是这个思路的最好体现。

HDU1760

就是DFS+那个思路

<span style="font-size:12px;">#include<stdio.h>
#include<string.h>
char  map[53][53];
int n,m;
int ok(int i,int j)
{
    if(map[i][j]=='0'&&map[i][j+1]=='0'&&map[i+1][j]=='0'&&map[i+1][j+1]=='0')
    {
        map[i][j]=map[i][j+1]=map[i+1][j]=map[i+1][j+1]='1';
        return 1;
    }
    return 0;
}
void hf(int i,int j)
{
    map[i][j]=map[i][j+1]=map[i+1][j]=map[i+1][j+1]='0';
}
int dfs()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(ok(i,j))
            {
                if(!dfs())
                {
                    hf(i,j);
                    return 1;
                }
                hf(i,j);
            }
        }
    }
    return 0;
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        memset(map,0,sizeof(map));
        for(int i=0;i<n;i++)
        {
            scanf("%s",map[i]);
        }
        if(dfs())
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}</span>

拿这个题目来说,lele先手,那么站在lele的立场上,如果有一种情况不是必败的,那么就可以赢,找到赢的情况就不必再找了。


相对于这个题目来说这种理解是对的,但是学长可能是感觉似乎有点毛病,让我去做做1907,是一个nim博弈的变形,是谁先取完谁输。大约是万一我的理解比较片面或者出错以后比较麻烦了。

HDU1907这个题目吧。我感觉就是一个nim博弈吧,直接全部异或后,正常的nim博弈异或后全部为0是先手的赢,这个变为先手输就好了嘛。

第二组样例都没过=_=|;然后发现如果输入n个1的话会出现错误。

对全部是1进行单独处理,如果n是偶数那么是先手的赢,反之则是先手输。

刚好计算SG值对于偶数个1是0,奇数个是1.那么只需要判断是否全部为1,然后反着输出就好了。

#include<stdio.h>
#include<string.h>
int main()
{
    int T,n,a;
    scanf("%d",&T);
    while(T--)
    {
        int flag=0,s=0,i;
        scanf("%d",&n);
        for(i=0;i<n;i++)
        {
            scanf("%d",&a);
            if(a>1)
                flag=1;
            s=s^a;
        }
        if(flag)
            puts(s?"John":"Brother");
        else
            puts(s?"Brother":"John");
    }
    return 0;
}

这个1079讨论版有比我更详细的说明推导过程,初始没有考虑分析中说的第三种过程,WA到死。

转自HDU讨论版:http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=17033&messageid=1&deep=0

说一下此题的为什么:
1.把月和日的和看做一个数sum,记为sum= (m + d),其中m为月份,d为日期。
  显然sum不是奇数就是偶数,sum一次变化后是奇数还是偶数呢?
  sum的变化有三种情况:
  (1)m + (d + 1) 即日期加一,且没有超出本月的日期,变化后为奇数。
  (2)(m + 1) + d 即月份加一(12月每一天都可到1月的一天),变化后为奇数。
  (3)(m + 1) + 1 即日期加一,但是超出本月的日期,变化后暂时不知道是奇数还是偶数。 
  由上面的(1)(2)(3)知道,如果sum为偶数且(1)或(2)可行,那么sum必然还是偶数。
  如果sum只有变化(3)可行,则使得这样的sum成立的m和d只能是如下几个:
  (m,d)={(1,31)(3,31)(5,31)(8,31)(10,31)}.
  如果sum为偶数且只有变化(3)可行,则使得这样的sum成立的m和d只能是如下几个:
  (m,d)={(1,31)(3,31)(5,31)}.为了使 ((m + 1) + 1)=(m + 2) 为奇数,则
  m必须为奇数,显然{(1,31)(3,31)(5,31)}的所有m都为奇数,即如果sum为偶数且
  只有变化(3)可行时,sum变化后也为奇数。
  所以可以得到如下结论:
      如果sum为偶数,那么一次变化后总可以使得sum变为奇数。
2.如果sum是奇数,经过一次变化之后是不是可以仍然是奇数呢?
  由于sum的三种变化中,(1)(2)使得sum变化之后成为了偶数,所以为了满足让其仍然为奇数,
  显然,我们只能寄希望于变化(3).而能进行变化(3)的(m,d)只有如下几个:
  (m,d)={(1,31)((2,28)or(2,29)(leap year))(3,31)(4,30)(5,31)
         (6,30)(7,31)(8,31)(9,30)(10,31)(11,30)(12,31)}.
  由于sum为奇数,那么m和d的组合只能是如下几个:
  (m,d)={(2,29)(8,31)(9,30)(10,31)(11,30)(12,31)}.同样为了使 (m + 2) 为奇数,
  则m必须为奇数,显然只有{(9,30)(11,30)}的m为奇数,即如果sum为奇数,经过一次变化之
  后可以仍然是奇数的(m,d)只有{(9,30)(11,30)}.
3.以上飞了很多的唾沫,显然都是一些铺垫,我们需要和本题来一个完美的对接。
  题目中我们的目的是使得 Adam (moving first)赢,目标状态的sum = 15 = (11 + 4),
  目标sum为一个奇数,如果初始数据的sum为偶数或者{(9,30)(11,30)},则Adam 总是可以
  将一个sum为奇数的(m,d)抛给Eve.那么Eve就永远只有将奇数sum变化成偶数sum的份,
  也就是说Eve不可能到达目标状态。反过来如果 Adam 一开始没有得到偶数或者{(9,30)(11,30)}
  状态,那么Adam就把偶数的sum抛给了Eve,Eve得到了控制权。Adam自然也不可能到目标状态。
  可能有人会想,Adam把奇数的sum抛给了Eve,那么Eve就不知道把奇数的sum继续变成奇数给
  Adam吗?
  对于这个问题,只要Adam不留余地就可以了,因为(9,30)前一个状态可能是{(8,30)(9,29)}.
  而这两个状态是可以绕过(9,30)的,同理也可以绕过(11,30).
  为什么完毕。

附上代码:

#include<stdio.h>
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||(m==9||m==11)&&d==30)
            puts("YES");
        else
            puts("NO");
    }

    return 0;
}

你可能感兴趣的:(小结,博弈)