博弈論(取石子專題)
有一堆石子共有N個。A B兩個人輪流拿,A先拿。每次最少拿1顆,最多拿K顆,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N和K,問最後誰能贏得比賽。
必勝策略:令 n = (k + 1) * r + s ; A第一次取s個,讓B面對k+1倍數的局面,如果B取m個則A取k+1 - m個。
#include <cstdio> #include <cstring> using namespace std; int main() { int t; scanf("%d", &t); while( t-- ) { int n, k; scanf("%d%d", &n, &k); if( n <= k ) printf("A\n"); else { int tmp = n % (k + 1); if( tmp == 0 ) printf("B\n"); else printf("A\n"); } } return 0; }
有一堆石子共有N個。A B兩個人輪流拿,A先拿。每次只能拿1,3,4顆,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N,問最後誰能贏得比賽。
分析:連續找一些書測試,可以發現當 n % 7 == 0 || == 2 時B勝,其餘情況A勝
#include <cstdio> using namespace std; int main() { int t, n; scanf("%d", &t); while( t-- ) { scanf("%d", &n); int tmp = n % 7; if( tmp == 0 || tmp == 2 ) printf("B\n"); else printf("A\n"); } return 0; }
有N堆石子。A B兩個人輪流拿,A先拿。每次只能從一堆中取若干個,可將一堆全取走,但不可不取,拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出N及每堆石子的數量,問最後誰能贏得比賽。
例如:3堆石子,每堆1顆。A拿1顆,B拿1顆,此時還剩1堆,所以A可以拿到最後1顆石子。
分析:尼姆博弈,對於一個Nim遊戲的局面(a1,a2,...,an),它是P-position當且僅當a1^a2^...^an=0,其中^表示異或(xor)運算.
#include <cstdio> using namespace std; int main() { int n; while( ~scanf("%d", &n) ) { int ans = 0; int num; for( int i=0; i<n; i++ ) { scanf("%d", &num); if( i == 0 ) ans = num; else ans ^= num; } if( ans != 0 ) printf("A\n"); else printf("B\n"); } return 0; }
有2堆石子。A B兩個人輪流拿,A先拿。每次可以從一堆中取任意個或從2堆中取相同數量的石子,但不可不取。拿到最後1顆石子的人獲勝。假設A B都非常聰明,拿石子的過程中不會出現失誤。給出2堆石子的數量,問最後誰能贏得比賽。
例如:2堆石子分別為3顆和5顆。那麼不論A怎樣拿,B都有對應的方法拿到最後1顆。
分析:威佐夫博弈
兩個人如果都採用正確操作,那麼面對非奇異局勢,先拿者必勝;反之,則後拿者取勝. 局勢(N,M)(N<M)滿足
N==(int)((1.0+sqrt(5.0))/2.0*(M-N))時,該局勢為奇異局勢,後拿者必勝.
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while( t-- )
{
int a, b; // n m
scanf("%d%d", &a, &b);
if( a > b )
swap(a, b);
int tmp = (int) ((1.0 + sqrt(5.0)) / 2.0 * (b - a)); // 奇異局勢
if( a == tmp )
printf("B\n");
else
printf("A\n");
}
return 0;
}