LeetCode進階1025-動態規劃

原題

1025. Divisor Game

Alice and Bob take turns playing a game, with Alice starting first.算法

Initially, there is a number N on the chalkboard. On each player's turn, that player makes a move consisting of:數組

  • Choosing any x with 0 < x < N and N % x == 0.
  • Replacing the number N on the chalkboard with N - x.

Also, if a player cannot make a move, they lose the game.bash

Return True if and only if Alice wins the game, assuming both players play optimally.微信

Example 1:ui

Input: 2 Output: true Explanation: Alice chooses 1, and Bob has no more moves. Example 2:spa

Input: 3 Output: false Explanation: Alice chooses 1, Bob chooses 1, and Alice has no more moves.code

Note:cdn

1 <= N <= 1000遊戲

1025. 除數博弈

Alice和Bob輪流玩一個遊戲,Alice先手。ci

一開始,黑板上有一個數字N.在某個玩家的回合中,該玩家進行如下操做:

  • 選擇任意整數x,x知足0 < x < N 而且N % x == 0
  • 將黑板上的數字N替換成N - x

最後,若是玩家沒法選擇,那麼判斷他遊戲失敗。

當且僅當Alice贏得比賽時才返回True,假設兩個玩家都達到最佳狀態

示例 1:

輸入:2 輸出:true 解釋:Alice選擇 1,Bob勃沒法進行操做。 示例 2:

輸入:3 輸出:false 解釋:Alice選擇 1,Bob勃也選擇 1,而後Alice沒法進行操做。

提示:

1 <= N <= 1000

  • 本題在LeetCode上在動態規劃分類下

題意分析

結合LeetCode的官方分類能夠猜想出本題解題思路偏向動態規劃思想,動態規劃的思想核心,結果之間具備互相依賴性。至少有一種思路是動態規劃方向(若是瞭解過斐波那契數字的解法,便很容易聯想到本題動態規劃的具體思路。未理解過斐波那契數解法的讀者請關注筆者leetcode第509題後續博文。)。另外一種思路,根據題目不妨帶入具體數字不難發現一個結論,偶數狀況必定會贏,奇數狀況必定會輸。咱們考慮如何證實這個結論。

方法一:動態規劃

思路:

當N爲1:

無數字x可帶入,輸

當N爲2:

帶入數字x=1能贏。

當N爲3:

若x爲1,對手回合新N爲2,根據上述推導結論N爲2時對手贏,而本身輸,繼續嘗試改變x

若x爲2,沒法整除捨棄

當N爲4:

若x爲1,對手回合新N爲3,根據上述推導結論N爲3時對手輸,而本身贏,無需繼續嘗試改變x;

僞代碼:

一、聲明一個大小爲N+1的boolean數組,用於存取從1~N每一個數組對應輸贏,數組下標表示數組,值表示輸贏(初始win[1]默認爲輸)
     二、雙重循環遍歷:
       第一層循環從數字2到數組N循環遍歷,按照從小到大;
       第二層循環從1到當前外層循環數組x(不包含x),按照從小到大;
       i.n % x ==0 能整除時,斷定對手的下一回合win[n-x]輸贏,若對手輸則天然當前回合能贏,將結果贏存入數組win[n]
       ii.不然,x自增後繼續嘗試i;
       
     三、遍歷結束從2到N的輸贏結果均保留在數組中,返回win[N];
   
複製代碼
  • 注意: 動態規劃總體上提交結果正確,可是也因爲循環次數較多,在時間空間複雜度等效率方面並不高。實際項目需結合具體場景進行算法選型

動態規劃實現代碼:

public boolean divisorGame(int N) {
		boolean[] win = new boolean[N + 1];
		for (int n = 2; n <= N; ++n) {
			for (int x = 1; x < n; x++) {
				if (n % x == 0) {
					if (!win[n - x]) {
						win[n] = true;
						break;
					}
				}
			}
		}
		return win[N];
	}

複製代碼

方法二:根據奇偶規律證實

思路:

當N爲1:贏

當N爲2:贏

當N爲3:輸

當N爲4:贏

...

發現規律N爲奇數贏,N爲偶數輸,則轉化爲數學問題證實題。

證實:

1、N爲奇數時

1.N爲質數,則x必然只能爲1,則對手回合N變成 N - 1 爲數字更小的偶數;
2.N不爲質數,存在x,則x必然爲奇數,對手回合N變成 N - x 爲數字更小的偶數;
結論:N爲奇數時,對手回合必然爲偶數且N減少。

2、N爲偶數時我方贏

證實:
1.令x等於1,對手回合N變成 N - 1 爲數字更小的奇數;
2.對手回合的N必然爲奇數,由一可知操做後我方的N必然爲減少的偶數,我方持續令x=1;
3.最終回合對手N減少爲1,對手輸而我方贏;
結論:N爲偶數時我方贏。

2、N爲奇數時我方輸

證實:
1.N爲質數,則x必然只能爲1,而對手回合則變成偶數,由一可知,對手令x=1對手贏,我方輸;
2.N不爲質數,存在x,則x必然爲奇數,對手回合N變成 N - x 爲數字更小的偶數,由一可知,對手令x=1對手贏,我方輸;
結論:N爲奇數時我方輸。

複製代碼

實現代碼:

public boolean divisorGame(int N) {
		return (N & 1) == 0;
	}
	
複製代碼

彩蛋

仔細觀察第二種方法實現的時候,並非使用N % 2 == 0來判斷奇偶,而是經過(N & 1) == 0來判斷,這即是本文的彩蛋。千里之行,始於足下,共勉之~

Alt

掃一掃 關注個人微信訂閱號
相關文章
相關標籤/搜索