引言
本文主要简单介绍博弈论中的著名问题–取石子,成为获胜者的条件都是取走最后一个石子
Nim游戏
关键词:N堆、第i堆石子有Ai个、每次可以任选一堆石子,至少取走1个石子
给定N堆石子,第i堆石子有Ai个。两名玩家轮流行动,每次可以任选一堆,取走任意多个石子,可把一堆取光,但不能不取。取走最后一个石子获胜。两人都采取最优策略,问先手是否必胜。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int ans = 0,a,n;
Scanner scanner=new Scanner(System.in);
n= scanner.nextInt();//石子堆数
while(n>0){
a=scanner.nextInt();//每一堆石子个数
ans^=a;
n--;
}
if(ans==0) System.out.println("No\n");//表示先手输
else System.out.println("Yes\n");//表示先手赢
}
}
巴什博弈
关键词:1堆石子、取得石子个数为[1,m]
有 1 堆石子,总个数是 n ,两名玩家轮流在石子堆中拿石子,每次至少取 1 个,至多取 m 个。取走最后一个石子的玩家为胜者。
结论: n%(m+1)=0时,先手必败;否则先手必胜。
证明:
①当m>=n时,显然先手必胜。
②当m+1=n时,无论先手取多少个,后手都能一次全部取完,先手必败。
③当n%(m+1)=0时,假设先手取了x个,那么后手一定可以取(m+1)-x个。因此无论先手怎么取,聪明的后手都会使得最后的物品总数量是m+1的倍数,慢慢的就会到②的情况,因此先手必败。
④当n%(m+1)!=0时,先手就可以先取n%(m+1)个物品,这样就会丢下n%(m+1)=0的局面给对面,那么后手必败,先手必胜
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int n,m;
Scanner scanner=new Scanner(System.in);
n = scanner.nextInt();
m=scanner.nextInt();
if(n%(m+1)==0) System.out.println("No\n");//表示先手输
else System.out.println("Yes\n");//表示先手赢
}
}
威佐夫博弈
关键词:两堆石子、每次可以在一堆中取,或者从两堆中取走相同个数的石子(数量不限)
有两堆石子,石子数可以不同。两人轮流取石子,每次可以在一堆中取,或者从两堆中取走相同个数的石子,数量不限,取走最后一个石头的人获胜。
import java.util.Scanner;
import static java.lang.Math.sqrt;
public class Main {
public static void main(String[] args) {
int n,m;
Scanner scanner=new Scanner(System.in);
n = scanner.nextInt();
m=scanner.nextInt();
if(n>m){
int temp=n;
n=m;
m=temp;
}
int ans= (int) ((m-n)*((1.0+sqrt(5))/2.0));
if(ans==n) System.out.println("No\n");//表示先手输
else System.out.println("Yes\n");//表示先手赢
}
}
斐波那契博弈
关键词:一堆石子、先手第一次取的石子数为[1,n)、之后每次取得石子数为[1,2*对手上一次取的石子数]
一堆个数为n(n≥2)的石子,游戏双方轮流取石子,规则如下:
先手不能在第一次把所有的石子取完,至少取1颗;
之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。
结论:
当且仅当石子数为斐波那契数时,先手必败。
import java.util.HashMap;
import java.util.Scanner;
import static java.lang.Math.sqrt;
public class Main {
static int[] f = new int[1005];
static HashMap<Integer,Integer> map=new HashMap();
public static void main(String[] args) {
int n;
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
solve();
if (map.containsKey(n)) System.out.println("No");//表示先手输
else System.out.println("Yes");//表示先手赢
}
//构造斐波那契数列
public static void solve() {
f[1] = f[2] = 1;
for (int i = 3; i <= 1000; ++i) {
f[i] = f[i - 1] + f[i - 2];
map.put(f[i],1);
}
}
}