尼姆博弈
尼姆博奕(Nimm Game):有三堆各若干個物品,兩個人輪流從某一堆取任意多的
物品,規(guī)定每次至少取一個,多者不限,最后取光者得勝。
這種情況最有意思,它與二進制有密切關系,我們用(a,b,c)表示某種局勢,首
先(0,0,0)顯然是奇異局勢,無論誰面對奇異局勢,都必然失敗。第二種奇異局勢是
(0,n,n),只要與對手拿走一樣多的物品,最后都將導致(0,0,0)。仔細分析一
下,(1,2,3)也是奇異局勢,無論對手如何拿,接下來都可以變?yōu)椋?,n,n)的情
形。
計算機算法里面有一種叫做按位模2加,也叫做異或的運算,我們用符號(+)表示
這種運算。這種運算和一般加法不同的一點是1+1=0。先看(1,2,3)的按位模2加的結
果:
1 =二進制01
2 =二進制10
3 =二進制11(+)
———————
0 =二進制00(注意不進位)
對于奇異局勢(0,n,n)也一樣,結果也是0。
任何奇異局勢(a,b,c)都有a(+)b(+)c =0。
如果我們面對的是一個非奇異局勢(a,b,c),要如何變?yōu)槠娈惥謩菽兀考僭Oa < b
< c,我們只要將c變?yōu)閍(+)b,即可,因為有如下的運算結果: a(+)b(+)(a(+)
b)=(a(+)a)(+)(b(+)b)=0(+)0=0。要將c變?yōu)閍(+)b,只要從c中減去c-(
a(+)b)即可。
例1。(14,21,39),14(+)21=27,39-27=12,所以從39中拿走12個物體即可達
到奇異局勢(14,21,27)。
例2。(55,81,121),55(+)81=102,121-102=19,所以從121中拿走19個物品
就形成了奇異局勢(55,81,102)。
例3。(29,45,58),29(+)45=48,58-48=10,從58中拿走10個,變?yōu)椋?9,4
5,48)。
例4。我們來實際進行一盤比賽看看:
甲:(7,8,9)->(1,8,9)奇異局勢
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇異局勢
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇異局勢
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇異局勢
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇異局勢
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇異局勢
甲勝。
總結:奇異局勢(異或為0) 先手敗 非奇異局勢 先手勝
HDU 1849 走棋游戲
1、棋盤包含1*n個方格,方格從左到右分別編號為0,1,2,…,n-1;
2、m個棋子放在棋盤的方格上,方格可以為空,也可以放多于一個的棋子;
3、雙方輪流走棋;
4、每一步可以選擇任意一個棋子向左移動到任意的位置(可以多個棋子位于同一個方格),當然,任何棋子不能超出棋盤邊界;
5、如果所有的棋子都位于最左邊(即編號為0的位置),則游戲結束,并且規(guī)定最后走棋的一方為勝者。
如果每次都是Rabbit先走棋
Sample Input
2
3 5
3
3 5 6
0
Sample Output
Rabbit Win!
Grass Win!
1 # include <iostream>
2 # include <cstdio>
3 # include <cmath>
4 # include <algorithm>
5 using namespace std ;
6
7 int main()
8 {
9 int ans,n,a;
10 while(scanf("%d",&n),n)
11 {
12 ans=0;
13 while(n--)
14 {
15 scanf("%d",&a);
16 ans ^= a;
17 }
18 if(ans==0) printf("Grass Win!
");
19 else printf("Rabbit Win!
");
20 }
21 return 0;
22 }
View Code
HDU 1850
桌子上有M堆撲克牌;每堆牌的數(shù)量分別為Ni(i=1…M);兩人輪流進行;每走一步可以任意選擇一堆并取走其中的任意張牌;桌子上的撲克全部取光,則游戲結束;最后一次取牌的人為勝者。
現(xiàn)在我們不想研究到底先手為勝還是為負,我只想問大家:
——“先手的人如果想贏,第一步有幾種選擇呢?”
求有幾種方案將 非奇異局勢 轉(zhuǎn)化為 奇異局勢
Sample Input
3
5 7 9
0
Sample Output
1
1 # include <iostream>
2 # include <cstdio>
3 using namespace std ;
4
5 int a[110] ;
6
7 int main ()
8 {
9 int n ;
10 while (scanf("%d" , &n) ) {
11 if (n == 0)
12 break ;
13
14 int ans = 0 ;
15 int sum = 0 ;
16
17 int i ;
18 for (i = 1 ; i <= n ; i++)
19 {
20 scanf("%d" , &a[i]) ;
21 ans ^= a[i] ;
22 }
23 for (i = 1 ; i <= n ; i++)
24 {
25 if (a[i] > (ans ^ a[i]))
26 sum++ ;
27 }
28
29 printf("%d
" , sum) ;
30
31 }
32 }
View Code
14年省賽 路邊騙局
作為一個江湖騙子,night_watcher又在路邊行騙了。現(xiàn)在他正在路邊向路人介紹他的新游戲:
有N堆石子 兩個人輪流對其操作 。操作分為兩步 第一步是每個人必須執(zhí)行的:從某堆石子中取一部分(至少一個) 丟棄;第二步可以選擇執(zhí)行或不執(zhí)行:從之前操作的那堆中拿一部分出來構成新堆。兩個人輪流操作,不能操作的人被認為輸。
現(xiàn)在給出N堆石子每一堆的個數(shù),假設每次都是路人先操作,且兩人都足夠聰明,請問路人能否取勝。
樣例輸入
3
1 2 7
樣例輸出
Yes
1 # include <cstdio>
2 # include <algorithm>
3 using namespace std ;
4
5 int n ;
6
7
8 int main ()
9 {
10 while (~scanf("%d" , &n))
11 {
12 int i , x ;
13 int sum = 0 ;
14 for (i = 1 ; i <= n ; i++)
15 {
16 scanf("%d", &x) ;
17 sum ^= x ;
18 }
19 if (sum)
20 printf("Yes
") ;
21 else
22 printf("No
") ;
23
24 }
25
26
27 return 0 ;
28 }
View Code
總結
- 上一篇: 动脉粥样硬化能不能吃烙饼
- 下一篇: Win11 Beta 22635.364