題目為:
牛牛和羊羊吃草,有t捆,每捆n個,兩個人玩一個遊戲,規則為,每個人每次隻能吃4的幂,即1個,4個,或者16個。。。,直到無草可吃為輸,每個人均會按照最佳政策進行。
輸入t為堆數,之後輸入每堆的個數;
如果牛牛赢,輸出niu
如果羊羊赢,輸出yang
5
1
2
3
4
5
niu
yang
niu
niu
yang
典型的NIM問題,博弈問題,對應每個石子數n可以分為兩種狀态:
N:先手赢
P:後手赢
- (n)代表此刻的石子數量,因為每次可以拿1,4,16,...對應的子狀況為(n-1),(n-4),(n-16)
- (0)時,自己沒有石子可拿,一定對手赢,狀态為P
- (1)時,自己全拿走一定赢,先手赢,狀态N
- (2)時,自己拿一個,對手拿走一個,自己肯定輸,(2)對應子狀态(1)為N,是以(2)為P
- (3)時,對應子狀态為(2)狀态為P,是以(3)為N
- (4)時,對應子狀态為(0)-》P,(3)-》N,考慮到每個人會選擇最佳政策,為了赢選擇P(0),此時變為N
- (5)時,可以選擇拿1個,對應(4)為N 拿4個,對應(1)為N,都是先手赢,是以(5)為P,是後手赢
- (6)時,可以選擇拿1個,對應(5)為P, 拿4個,對應(2)為P,都是後手赢,是以(6)為N,是先手赢
- ….
- 以此類推,(17)時,子狀态(1) (13) (16)..
N先手赢為1,P後手赢為0,目前石子為n時狀态為NIM(n),則狀态轉移方程為
NIM(n-1) NIM(n-4) NIM(n-16) NIM()… NIM(n)
1 1 1 1 0
0 0 0 0 1
0 0 0 1 1
0 0 1 1 1
核心是
隻要子狀态包含P後手赢,那麼為了赢,一定會選擇這個狀态為P的子狀态,讓自己赢
如果子狀态都是N先手赢,那麼無論怎麼做,輪到下一個都必赢,自己一定會輸。
import java.util.Scanner;
/**
* Created by Luna on 2017/10/14.
*/
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int loop = input.nextInt();
int c=0;
int[] num = new int[loop];
while (c<loop){
num[c]=input.nextInt();
c++;
}
for (int i = 0; i < loop; i++) {
System.out.println(NIM(num[i])?"niu":"yang");
}
}
public static boolean NIM(int k) {
if (k==0)
return false;
if (k==1)
return true;
int tmp=1;
int count=-1;
while(tmp<=k){ //判斷輸入值包含4的k次幂,對應有k+1個子狀況
if(tmp==k)
return true;
tmp<<=2;
count++;
}
for (int i = 0; i <=count ; i++) {
int next=k - (int)Math.pow(4, i);
if(!NIM(next)) return true; //隻要包含false 那麼就選擇它,讓自己赢
}
return false;//如果子狀況都是先手赢,那麼自己必輸。
}
}
運作結果:
5
1
2
3
4
5
niu
yang
niu
niu
yang
5
16
19
31
42
76
niu
niu
niu
yang
niu