UVA-10404 Bachet's Game

這是一道很經典的必勝策略題目

原題:Stan和Ollie在玩一個遊戲,桌子上有n(n <= 1e6)枚硬幣,同時給出k(k <= 10)個互不相同的正整數數a[1], a[2],  a[3], ..., a[k],Stan跟Ollie輪流拿桌子上的硬幣,但每次只能拿這k個數中的一個數的枚數(即設這k個數構成一個集合,Stan跟Ollie只能從這集合中挑出一個數來作爲他本回合拿走硬幣的數量),每一局遊戲都是Stan先拿,這k個數中必有1,所以每一局遊戲必然能分出勝負

現在定你n, k, 以及後邊的k個數,假設他們兩個每一步都做出了最佳策略,那麼請問最終會是誰獲勝?

輸入:

有多行,每行第一個數是n,第二個數是k,然後後邊接着k個數

輸出:

對於每行都要輸出一個結果,即輸出"Stan wins"或者"Ollie wins"

樣例輸入:

20 3 1 3 8

21 3 1 3 8

22 3 1 3 8

23 3 1 3 8

1000000 10 1 23 38 11 7 5 4 8 3 13

999996 10 1 23 38 11 7 5 4 8 3 13

樣例輸出:

Stan wins

Stan wins

Ollie wins

Stan wins

Stan wins

Ollie wins

 

原題鏈接(英文):https://vjudge.net/problem/UVA-10404


這道題,實際上是一道DP題,狀態status[j]表示輪到Stan回合時,桌子上剩下j枚硬幣,此時Stan是處於必勝態還是處於必敗態

那麼很顯然status[0]的狀態應該是必敗態。

可是狀態如何轉移呢?

我們現在看向status[j]這個狀態,我們看看這個狀態下Stan必勝或者必敗是怎麼決定下來的(status[j]的值是怎麼確定的):

現在桌子上有j枚硬幣,Stan盯着這些硬幣,思考着怎樣拿他才能獲勝

思考一番後,他突然明白,只要他拿走硬幣後,剩下的硬幣數量是對手的必敗態,那麼他就能獲勝,用數學的形式去表達的話就是,設1 <= i <= k,如果存在一個a[i],使得status[j - a[i]] == 必敗態,那麼status[j]就一定是必勝態

相反,如果不存在status[j - a[i]] == 必敗態,也就是說,不管Stan怎麼拿硬幣,輪到Ollie的回合時Ollie都能處在必勝態的話,那麼Stan此時是處於必敗態,也就是status[j] = 必敗態。


這裏可能有點難理解,爲什麼這個status[j - a[i]]的必敗態能表示Ollie的必敗態,當初status不是用來表示Stan的狀態的麼?雖然從最終結果上看整個status是在表示Stan的狀態的,但在實際的計算過程中,當桌子上剩餘x枚硬幣時,不論是輪到誰,status[x]都表示他當前所處的狀態是必勝還是必敗,也就是說,之所以我們最終結果看整個status是在表示Stan的狀態,是因爲我們默認上是對於整個status的狀態下都是Stan先手,所以這個數組才都表示Stan的狀態的。這樣一來,status[j - a[i]]能表示Ollie的狀態也能解釋得清了,因爲當前輪到Stan的回合下,桌子上剩下j枚硬幣,當Stan拿走了a[i]枚硬幣後,此時桌子上剩下j - a[i]枚硬幣,是Ollie的回合,所以這時候的status[j - a[i]]所表示的,就是Ollie的狀態。


知道必勝必敗態之間怎麼轉移後,代碼也就好寫了

for (int j = 1; j <= n; j++)
	for (int i = 0; i < k; i++)
		if (j - a[i] >= 0 && status[j - a[i]] == Lose) status[j] = Wins;

整個代碼:

#include<iostream>
#include<cstring>
#define Wins true
#define Lose false
using namespace std;

const int Maxn = 1e6 + 10;
bool status[Maxn];
int a[15];

int main()
{
	int n;
	while (cin >> n) {
		memset(status, 0, sizeof(status));
		int k;
		cin >> k;
		for (int i = 0; i < k; i++)
			cin >> a[i];

		for (int j = 1; j <= n; j++)
			for (int i = 0; i < k; i++)
				if (j - a[i] >= 0 && status[j - a[i]] == Lose) status[j] = Wins;

		if (status[n] == Wins) cout << "Stan wins" << endl;
		else cout << "Ollie wins" << endl;
	}

	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章