二進制
轉換
二進制轉十進制
:數字x基數^位次冪+
例如:二進制數0101用十進制表示的數就是:1x2^0 + 0x2 ^1 + 1x2 ^2 + 0x2 ^3 = 5
十進制轉二進制1.(短除法)
:除2取余(從下往上取)
十進制轉二進制2.
:與二進制轉十進制相反
七種位運算
& :按位與(AND)(∪)
a&b 把ab轉化為二進制,對於每一位,如果ab這一位都是1,這該位是1,否者為0.將所有位的結果累加
- 遇0則0。如:1 & 1=1,1 & 0=0。只要有0結果就是0。
| :按位或(OR)(∩)
a | b 把ab轉化為二進制,對於每一位,如果ab至少有一個是1,則該位結果是1,否者為0.將所有位的結果累加
- 遇1則1。如:1 | 1=1,1 | 0=1。只要有1結果就是1。
~:取反(NOT)(¬)
,1-0 0-1
^ :按位異或(XOR)(⊕)
a ^ b 把a b轉化為二進制,對於每一位,如果a b恰好有一個是1,則該位結果是1,否者為0.然后將結果轉化為十進制
- 口訣:不進位加(相同為0,相異為1)。如:1 ^ 0=1,1 ^ 1=0。
" >> ":右移
a>>b 把a轉化為二進制,把所有數位向右移動b個位置,相當於a / 2的b次冪
" << ":左移
a<<b 把a轉化為二進制,把所有數位向左移動b個位置,新多的位置補零 ,相當於a * 2的b次冪
" >>> ":無符號右移
。不管符號位是0還是1,都是補0。
博弈論Nim取子問題簡單理解(證明)
轉: 鏈接.
基礎知識
- 通常會將先手必敗的局面稱為奇異局勢。
- 減去某一個數,等價於將被減數當中若干個0變成1,1變成0。
- 可以證明:任何一個偶狀態在其中一個數變小后
一定成為
奇狀態,而一個奇狀態一定可以
通過改變一個數變成偶狀態.
先是3堆石子
關鍵:
- 我們有3個數,如果這三個數的每一位的1的數量和都是偶數,也就是數量和不是0就是2的話,那么這一定是一個奇異局面。
舉個例子,比如[10, 8, 2]是一個奇異局面,我們把它們寫成二進制。10的二進制是1010,8的二進制是1000,2的二進制是10。所以我們可以發現這三個數的二進制位加起來,第1、2、3位都出現了兩個1。這個時候先手不論如何操作,后手只需要保證剩下的三個數的二進制位維持這個特性即可。這樣做可以保證最后一次拿取結束之后,給先手留下[0, 0, 0]的局面。本質上來說,它的原理和兩堆石子的時候是一樣的,只不過轉化了一種形式。
舉個例子,比如我們從10當中拿走3顆石子,得到(7, 8, 2),我們觀察二進制位分別是111, 1000, 10。會發現每一位1的數量從低到高分別是[1, 2, 1, 1]。所以我們可以從1000拿取3個石子,保證留下的數量是101,也就是5。這樣剩下的1的個數就是[2, 2, 2],依然是偶數。所以先手不論如何拿,后手都可以保證一定可以讓留下的數字在二進制上保持偶數,先手一定必敗。在不滿足這個條件的局面當中先手一定必勝,因為先手可以在第一次通過拿取掉多余的1,保證留下一個必敗的局面給后手。
這也是這題的解法,即通過二進制位來判斷是否先手必勝。
- 判斷每個二進制位當中出現的1的次數和是否是偶數,可以通過位運算的
亦或
來完成。(在亦或操作當中,對每一個二進制位進行計算,奇數為1,偶數為0。所以我們只需要計算一下這三堆石子亦或之后的結果是否為0,就可以知道是否每一個二進制位的1的數量是否都是偶數了。)
def win_or_lose(a, b, c):
return (a ^ b ^ c) == 0
推廣 Bouton定理
定理的內容是
先手可以在非平衡的Nim博弈中取勝,
而后手可以在平衡的Nim博弈中取勝。
(這里的 平衡
就是指的是所有二進制位1的數量是偶數。)
def win_or_lose(nums):
ret = 0
for i in nums:
ret ^= i
return ret == 0
下面轉至鏈接: link.
一. 巴什博弈(Bash Game)
只有一堆n個物品,兩個人從輪流中取出(1~m)個;最后取光者勝。
考慮到 若n=m+1
那么 第一個人不論如何取都不能取勝。
進一步我們發現 若 n=k*(m+1)+r; 先取者拿走 r 個,那么后者再拿(1~m)個
n=(k-1)*(m+1)+s; 先取者再拿走s 個 最后總能造成 剩下n=m+1 的局面。
因此,此時先手有必贏策略。
相對應的,若n=k*(m+1) 那么先取者必輸。
因此我們可以寫出對應的程序(默認 n m都大於0)
int Bash_Game(int n,int m)
//是否先手有必贏策略
{
if (n%(m+1)!=0) return 1;
return 0;
}
二、尼姆博弈(Nimm Game)
題目: 當有N堆,每堆有Mi>0個物品,依舊是兩個人來取該怎么判斷?
int Nimm_Game(int n)//假設n個數存在數組f[]中,有必勝策略返回1
{
int flag=0;
for(int i=1;i<=n;i++)
flag^=f[i];
if(flag) return 1;
return 0;
}
三 威佐夫博奕(Wythoff Game)
問題: 有兩堆各若干個物品,
兩個人輪流從某一堆取同樣多的物品
--或 --同時從兩堆中取同樣多的物品,
規定每次至少取一個,多者不限,最后取光者得勝。
下面就沒學
作 者:Angel_Kitty
出 處:https://www.cnblogs.com/ECJTUACM-873284962/
關於作者:阿里雲ACE,目前主要研究方向是Web安全漏洞以及反序列化。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!
實戰.
B - Road to Arabella
Gym - 102263B
題意:
-
給一個n和k,
-
每個人每次可以1~data=max(1,n-k)中選一個數,然后把n減去這個數。(n實時更新)
-
n為0時游戲結束。
思路: -
當data=1時,只能選1。只要這時的n是偶數,那么每次輪到Ayoub選時n都是奇數,輪到Kilani選時n都是奇數,那么Ayoub是必勝的。
-
當n>k+1時Kilani永遠是必勝態,因為他可以讓自己選的時候n為奇數。
#include<iostream>
using namespace std;
int main() {
int t, n, k;
int flag;
cin >> t;
while (t--) {
flag = 1;
cin >> n >> k;
if ((n - k == 1 )||(n-k==0))
if(n % 2 == 0)flag = 0;
if (flag)cout << "Kilani\n";
else cout << "Ayoub\n";
}
}