算法分析李xx搶公章案

x總必勝無疑

理由是:從歷史上看比"奪門之變"更慘烈的"玄武門"之變,最後的勝利者可是姓"李"。

不開玩笑的說,這事可以用算法來證明。

最優策略(Optimal Strategy)

這場"奪門之變"對看客來說是吃瓜大戲。
對“x總”和“夫人”來說也就是一場博弈遊戲。
爲了更好的說明這個問題,我們可以把“這個瓜”映射到下面的模型上。

1 x總和夫人交替發招
2 可以從{"水軍":8 , “上訴”:15, “裁員”:3, "搶章":7 } 字典中選擇動作(現實情況可選擇的動作要比這個多很多)
  每個動作都會給選擇人帶來收益。我們把收益抽出來組成【8, 15, 3, 7】列表。
3 每個參與者必須根據對手的策略做出選擇,爲了接近真實情況,我們加入如下限制:
  只能從列表的頭部或者尾部選擇收益。
4 獲勝條件:最後收益最大的人獲勝。

我們就用這個小規模問題來演示最優策略思路

x總在[8, 15, 3, 7]中選擇7.
夫人在[8,15, 3] 中選擇8.
x總在[15, 3]中選擇15.
夫人最後只能選擇3了.
x總最後的收益爲: 22(7 + 15)

由於問題規模較小,這是我們用眼睛解題的過程。那如果規模較大呢?

輔助工具遞歸樹

在這裏插入圖片描述

  1. 我們定義函數os,它可以返回對應規模問題的最優解。
    本例中os(i=1, j=4) 代表問題規模爲從1到4

  2. 兩個規模之間存在的關係爲

os(i,j) = max(
            val[i] + min( os(val, i+1, j-1), os(val, i+2, j)),
            val[j] + min( os(val, i, j-2), os(val, i+1, j-1))
            )

在這裏插入圖片描述
參加遊戲的人都不是傻子,在做出本次選擇後,你的對手一定會在剩下的方案中去尋找最優解,所以你下一次能拿到的值,一定是os(i+2, j), os(i+1, j-1)中小的那一個。

  1. 退出條件
    I > J 此時全部問題處理完
    I == J 返回val[I]
    I == J+1 返回最後兩個中的最大值。

有了思想,代碼就不難了。

奉上代碼

def os(val, i, j):
    if i > j:
        return
    
    if i == j:
        return val[i]
    
    elif j == i + 1:
        return max(val[i], val[j])
    else:
        return max(
            val[i] + min( os(val, i+1, j-1), os(val, i+2, j)),
            val[j] + min( os(val, i, j-2), os(val, i+1, j-1))
            )
    

val = [8, 15, 3, 7]
i = 0
j = len(val)
res = os(val, i, j-1)
opponent=sum(val) - res
print("做爲先手你可以拿到:{}\n你的對手可以拿到:{}".format(res, opponent))

輸出:

做爲先手你可以拿到:22
你的對手可以拿到:11

這段代碼中,會有Overlapping Subproblems問題,優化一下爲:

def os(val, i, j):
    if i > j:
        return
    if dp[i][j] == -1:
        if i == j:
            dp[i][j] = val[i]
        elif j == i + 1:
            dp[i][j] = max(val[i], val[j])
        else:
            dp[i][j] = max(
                val[i] + min( os(val, i+1, j-1), os(val, i+2, j)),
                val[j] + min( os(val, i, j-2), os(val, i+1, j-1))
                )
    return dp[i][j]

val = [8, 15, 3, 7]
i = 0
j = len(val)
dp = [[-1]*j for _ in range(j)]
res = os(val, i, j-1)
opponent=sum(val) - res
print("做爲先手你可以拿到:{}\n你的對手可以拿到:{}".format(res, opponent))

對動態規劃細節感興趣,可以參考我以前的博文,鏈接我放到文章末尾。
用算法來觀察這個世界是不是很有趣?!

先下手爲強

像x總這樣的老江湖,能力自然不言而喻。這次能夠先出
手,發動雷霆一擊,一定在背後把所有細節推演過無數遍。
通過我們剛纔對最優策略學習,應該意識到:“這先下手的一方,只要不出現大失誤,x總基本就贏定了。先下手就是強。”

你支持誰?

如果這是一場零和博弈,你只能在x總和夫人中選一個人,你會支持誰?
請寫在評論區,讓世界聽見你的聲音!

當然也歡迎你把更好的解決思路,寫在評論區。
在這裏插入圖片描述


我的其他動態規劃文章
最火的瓜,得用動態規劃來吃
A姓女友,B姓女友,渣男與最長公共子串(有視頻)
社區舉辦“殺戮遊戲”,你能活下來嗎?

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