1. 程式人生 > >ACM之預測贏家

ACM之預測贏家

ACM 預測贏家 Java 極小化極大算法

題目如下

技術分享圖片技術分享圖片


零和博弈問題:表示所有博弈方的利益之和為零或者是一個常數,即一方有得其他方必有失,且在博弈中各方是不會合作的。解決此類問題的方法有極小化極大算法等,那就先來學習一下這個極小化極大算法: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之預測贏家