BZOJ3298: [USACO 2011Open]cow checkers 威佐夫博弈
Description
一天,Besssie准备和FJ挑战奶牛跳棋游戏。这个游戏上在一个M*N的棋盘上,
这个棋盘上在(x,y)(0<=x棋盘的左下角是(0,0)坐标,棋盘的右上角是坐标(M-1,N-1)。
Bessie每次都是第一个移动棋子,然后Bessie与Fj轮流移动。每一轮可以做以下三种中的一种操作:
1)在同一行,将棋子从当前位置向左移动任意格;
2)在同一列,将棋子从当前位置向下移动任意格;
3)将棋子从当前位置向下移动k格再向左移动k格(k为正整数,且要满足移动后的棋子仍然在棋盘上)
第一个不能在棋盘上移动的人比赛算输(因为棋子处在(0,0)点)。
共有T个回合(1<=T<=1,000),每次给出一个新起始点的坐标(x,y),确定是谁赢。
1<=M<=1,000,000;1<=N<=1,000,000
Input
第1行:两个用空格隔开的整数M和N;
第2行:一个整数T;
第3到第T+2行:两个用空格隔开的整数x和y.
Output
第1到T行:包含“Farmer John”或者是“Bessie”,表示谁赢了这轮游戏。
Sample Input
3 3
1
1 1
1
1 1
Sample Output
Bessie
Solution
威佐夫博弈板子(套公式$\frac{\sqrt{5}+1}{2}$)
把横坐标抽象成一堆石头,纵坐标抽象成一堆石头就行了
然后如果不知道威佐夫博弈这玩意的话其实可以用必胜态必败态来推一下
可以画个图什么的,把前几个必败态找出来,然后会发现对于每一列,必败态仅有一点,然后这些点都沿着对角线对称
所以就递推一下必败态就可以了
威佐夫博弈做法
#include <bits/stdc++.h> using namespace std ; int n , m , t ; int main() { int a , b ; scanf( "%d%d%d" , &a , &b , &t ) ; double y = ( sqrt( 5 ) + 1 ) / 2 ; while( t -- ) { scanf( "%d%d" , &n , &m ) ; if( m < n ) swap( m , n ) ; int x = ( m - n ) * y ; if( x == n ) puts( "Farmer John" ) ; else puts( "Bessie" ) ; } }
递推做法
#include <bits/stdc++.h> using namespace std ; const int N = 1e6 + 10 ; int ans[ N ] ; int main() { ans[ 1 ] = 1 ; int tmp = 1 ; for( int i = 1 ; i < N ; i ++ ) { if( ans[ i ] ) continue ; ans[ i ] = ans[ tmp ] + i - tmp + 1 ; tmp = i ; if( ans[ i ] < N - 1 ) ans[ ans[ i ] ] = i ; } int n , m , t ; scanf( "%d%d%d" , &n , &m , &t ) ; while( t -- ) { int a , b ; scanf( "%d%d" , &a , &b ) ; a ++ ; b ++ ; if( ans[ a ] == b || ans[ b ] == a ) puts( "Farmer John" ) ; else puts( "Bessie" ) ; } }