最近学了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>
相对于这个题目来说这种理解是对的,但是学长可能是感觉似乎有点毛病,让我去做做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; }
转自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; }