ACM之預測贏家
零和博弈問題:表示所有博弈方的利益之和為零或者是一個常數,即一方有得其他方必有失,且在博弈中各方是不會合作的。解決此類問題的方法有極小化極大算法等,那就先來學習一下這個極小化極大算法:Minimax算法,是一種找出失敗的最大可能性中的最小值的算法。
算法偽代碼(維基百科)
function minimax(node, depth) if node is a terminal node or depth = 0 return the heuristic value of node if the adversary is to play at node let α := +∞ foreach child of node α := min(α, minimax(child, depth-1)) else {we are to play at node} let α := -∞ foreach child of node α := max(α, minimax(child, depth-1)) return α
從偽代碼中可以看到:在對手回合時,算法默認對手的收益為無窮大;在自己回合時收益為無窮小。在叠代過程中,需要進行玩家判斷,因為遊戲的目的就是最小化對手的優勢而最大化自己的利益,所以在對手的回合時,對手需要讓我最小化利益min,而在自己的回合時,我肯定是最大化自己的利益,所以用max。那麽Max和Min分別都是怎麽計算出利益大小的呢?遞歸!不知道利益大小就遞歸地往下問,問到最後再遞歸地回答上來,利益就計算出來了
那針對這道題的解答思路就有了:
我們用一個solve(nums)函數來計算當前玩家從nums中可以獲得的最大收益,當收益>=0時,玩家1獲勝。
solve(nums) = max(nums[0] - solve(nums[1:]), nums[-1] - solve(nums[:-1]))
為什麽是nums[0]-solve(nums[1:])而不是+呢?因為玩家拿完一個數之後就輪到別人了,此時solve函數是別人的收益,我們用累計自己收益,並扣除對手收益的方式來表示自己的收益最後是否比對手多,如果結果大於0就表示比對手多。
(備註:nums[-1],nums[:-1]的意思:Python的索引方式,帶冒號的叫做數組的“切片”,冒號前的數是起點的索引,後面的是終點索引。如果沒有起點默認是0,如果沒有終點默認就是到最後。舉個栗子: nums[1,2,3,4,5],則 nums[-1] = 5 , nums[ :-1] = [1,2,3,4] , nums[ 1:] = [2,3,4,5] )
Java實現
public class PredictTheWinnerBter { public static boolean solution(int[] nums){ return helper(nums,0,nums.length - 1) >= 0; } private static int helper(int[] nums,int s,int e) { return s == e ? nums[e] : Math.max(nums[s] - helper(nums,s+1,e), nums[e] - helper(nums,s,e-1)); } public static void main(String[] args){ int[] nums = new int[]{1,5,255,2}; System.out.println(solution(nums)); } }
這道題剛開始真的不會寫,但這種題目可以說是非常經典了。還有解法是自上而下的DP(動態規劃),找時間研究一下。
參考:Leetcode解題報告
ACM之預測贏家