HDU 1907&2509
Nimm博弈的延伸。
转自:http://www.cppblog.com/klion/archive/2010/08/27/124932.html
学了三种简单博弈(前一篇)之后,我又在这篇博文这学了HDU1907的解法
下面说下我的理解,有些借鉴原博文。
这题和下面的题有点相似,但是又不一样
也就是说把最后取完的定为输家改成,最后取完的定为赢家。
后面的这个要简单一点,下面是简单分析,先来看这个简单的
首先我们用T表示当前状态的所有火柴数异或为0,否则极为S。
1.S可以转化成T
我们设一共有n堆火柴,每堆有k(i)根.
那么S态中k(1)^k(2)^……^k(n) != 0,这个值我们记为c
那么肯定有某个k(i)的最高位和c的最高位同为1,不然c的最高位变成了0
假设这个最高位和c的最高位同为1的是第m堆,这样我们可以得到x = k(m)^c < k(m)(x的最高位为0)
k(1)^k(2)^……^x^……^k(n)
=k(1)^k(2)^……^k(m)^c^……^k(n)
=k(1)^k(2)^……^k(m)^k(m+1)^……^k(n)^(k(1)^k(2)^K(3)^……^k(n))
= 0
这样我们只要从第m堆中取出k(m)-x根火柴,那么剩下的就变成了T态。
2.T一定会转化成S
假设T不转化成S,
我们从第m堆中取出一定的火柴数那么我们得到
0 = k(1)^k(2)^……^k(m)^……^k(n)
0 = k(1)^k(2)^……^k(m')^……^k(n)(k(m)表示取之前的 k(m')表示取最后的)
那么我们得到k(m)^k(m')=0也就是说k(m)==k(m')矛盾。
3.S态,只要方法得当就可以取胜
每一个S态可以转化成T态,然后每一个T态必然转化成S态,所以每一次只要把S态变成T态,那么对手就只能把T态变成S态,这样自己就一直控制着S态,最后一定可以取完(变成T态)成为胜利者
4.T态,只要对手方法得当就必输
同3
下面我们来看看该题的思路
我们设只有一根火柴的堆为单根堆,否则为充裕堆,充裕堆>=2的T用T2表示,全是单根堆的T用T0表示(没有T1)
同样的充裕堆>=2的S用S2表示,全为单根堆的S用S0表示,只有一堆充裕堆的用S1表示
5.S0必败,T0必胜
每次自己取奇数堆的,那么最后一堆一定由自己取.T0必胜同理
6.S1只要方法得当,必胜
如果单根堆的堆数为偶数,那么把充裕堆中取成只剩下1根,变成S0,对手必败.如果单根堆的堆数为奇数,那么把充裕堆全取完,同样对手必败
7.S2不可以一次转化到T0
每次最多只能取完一堆,所以2堆充裕堆不可能一次就没了
8.S2可以一次变成T2
用1可知S可以变成T,由7可知S不可一次变成T0,所以S可以一次变成T2
9.T2不可以一次变成S0
同7
10.T2一定变成S1或者S2中的一种
由2知T一定变成S,由9知T2不可一次变成S0,所以一定变成S1或S2中的一种
11.S2,只要方法得当一定胜
S2可以变成T2,然T2一定变成S1或者S2,如果变成S1,已胜,变成S2,则继续,直到T2只能变成S1为止。
12.T2,只要对手方法得当必输
同11
综上所述先手必胜态为T0 S2 S1
必输态为S0 T2
到这这两题就可以轻松搞定了。
下面说下我的理解,有些借鉴原博文。
这题和下面的题有点相似,但是又不一样
也就是说把最后取完的定为输家改成,最后取完的定为赢家。
后面的这个要简单一点,下面是简单分析,先来看这个简单的
首先我们用T表示当前状态的所有火柴数异或为0,否则极为S。
1.S可以转化成T
我们设一共有n堆火柴,每堆有k(i)根.
那么S态中k(1)^k(2)^……^k(n) != 0,这个值我们记为c
那么肯定有某个k(i)的最高位和c的最高位同为1,不然c的最高位变成了0
假设这个最高位和c的最高位同为1的是第m堆,这样我们可以得到x = k(m)^c < k(m)(x的最高位为0)
k(1)^k(2)^……^x^……^k(n)
=k(1)^k(2)^……^k(m)^c^……^k(n)
=k(1)^k(2)^……^k(m)^k(m+1)^……^k(n)^(k(1)^k(2)^K(3)^……^k(n))
= 0
这样我们只要从第m堆中取出k(m)-x根火柴,那么剩下的就变成了T态。
2.T一定会转化成S
假设T不转化成S,
我们从第m堆中取出一定的火柴数那么我们得到
0 = k(1)^k(2)^……^k(m)^……^k(n)
0 = k(1)^k(2)^……^k(m')^……^k(n)(k(m)表示取之前的 k(m')表示取最后的)
那么我们得到k(m)^k(m')=0也就是说k(m)==k(m')矛盾。
3.S态,只要方法得当就可以取胜
每一个S态可以转化成T态,然后每一个T态必然转化成S态,所以每一次只要把S态变成T态,那么对手就只能把T态变成S态,这样自己就一直控制着S态,最后一定可以取完(变成T态)成为胜利者
4.T态,只要对手方法得当就必输
同3
下面我们来看看该题的思路
我们设只有一根火柴的堆为单根堆,否则为充裕堆,充裕堆>=2的T用T2表示,全是单根堆的T用T0表示(没有T1)
同样的充裕堆>=2的S用S2表示,全为单根堆的S用S0表示,只有一堆充裕堆的用S1表示
5.S0必败,T0必胜
每次自己取奇数堆的,那么最后一堆一定由自己取.T0必胜同理
6.S1只要方法得当,必胜
如果单根堆的堆数为偶数,那么把充裕堆中取成只剩下1根,变成S0,对手必败.如果单根堆的堆数为奇数,那么把充裕堆全取完,同样对手必败
7.S2不可以一次转化到T0
每次最多只能取完一堆,所以2堆充裕堆不可能一次就没了
8.S2可以一次变成T2
用1可知S可以变成T,由7可知S不可一次变成T0,所以S可以一次变成T2
9.T2不可以一次变成S0
同7
10.T2一定变成S1或者S2中的一种
由2知T一定变成S,由9知T2不可一次变成S0,所以一定变成S1或S2中的一种
11.S2,只要方法得当一定胜
S2可以变成T2,然T2一定变成S1或者S2,如果变成S1,已胜,变成S2,则继续,直到T2只能变成S1为止。
12.T2,只要对手方法得当必输
同11
综上所述先手必胜态为T0 S2 S1
必输态为S0 T2
到这这两题就可以轻松搞定了。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 #include<stdlib.h> 5 #include<math.h> 6 #include<string.h> 7 #include<queue> 8 #include<map> 9 #include<vector> 10 #include<set> 11 #include<string> 12 13 #define repA(p,q,i) for( int (i)=(p); (i)!=(q); ++(i) ) 14 #define repAE(p,q,i) for( int (i)=(p); (i)<=(q); ++(i) ) 15 #define repD(p,q,i) for( int (i)=(p); (i)!=(q); --(i) ) 16 #define repDE(p,q,i) for( int (i)=(p); (i)>=(q); --(i) ) 17 #define swap(a,b) { int temp=(a) ; (a)=(b); (b)=temp; } 18 #define ll long long 19 #define range 50 20 21 int a[range]; 22 23 int main() 24 { 25 int test; scanf("%d",&test); 26 int n; 27 int num1,num2,num; 28 int result; 29 while(test--) 30 { 31 scanf("%d",&n); 32 result=num1=num2=0; 33 repA(0,n,i) 34 { 35 scanf("%d",&num); 36 if(num == 1) ++num1; 37 else ++num2; 38 result ^= num; 39 40 } 41 if(result == 0) 42 { 43 if(num1 == n) printf("John\n"); 44 else printf("Brother\n"); 45 } 46 else 47 { 48 if(num1 == n) printf("Brother\n"); 49 else printf("John\n"); 50 } 51 52 } 53 return 0; 54 } 55 56
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 #include<stdlib.h> 5 #include<math.h> 6 #include<string.h> 7 #include<queue> 8 #include<map> 9 #include<vector> 10 #include<set> 11 #include<string> 12 13 #define repA(p,q,i) for( int (i)=(p); (i)!=(q); ++(i) ) 14 #define repAE(p,q,i) for( int (i)=(p); (i)<=(q); ++(i) ) 15 #define repD(p,q,i) for( int (i)=(p); (i)!=(q); --(i) ) 16 #define repDE(p,q,i) for( int (i)=(p); (i)>=(q); --(i) ) 17 #define swap(a,b) { int temp=(a) ; (a)=(b); (b)=temp; } 18 #define ll long long 19 #define range 50 20 21 int a[range]; 22 23 int main() 24 { 25 //int test; scanf("%d",&test); 26 int n; 27 int num1,num2,num; 28 int result; 29 while(scanf("%d",&n) != EOF) 30 { 31 32 result=num1=num2=0; 33 repA(0,n,i) 34 { 35 scanf("%d",&num); 36 if(num == 1) ++num1; 37 else ++num2; 38 result ^= num; 39 40 } 41 if(result == 0) 42 { 43 if(num1 == n) printf("Yes\n"); 44 else printf("No\n"); 45 } 46 else 47 { 48 if(num1 == n) printf("No\n"); 49 else printf("Yes\n"); 50 } 51 52 } 53 return 0; 54 } 55 56
To Be The Best Of Yourself