日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划之四键键盘

發布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划之四键键盘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

動態規劃之四鍵鍵盤

如何在 N 次敲擊按鈕后得到最多的 A? 我們窮舉唄, 每次有對于每次按鍵, 我們可以窮舉四種可能, 很明顯就是?個動態規劃問題。

第?種思路(超時)

這種思路會很容易理解, 但是效率并不?, 我們直接?流程: 對于動態規劃問題, ?先要明?有哪些「狀態」 , 有哪些「選擇」 。

具體到這個問題, 對于每次敲擊按鍵, 有哪些「選擇」 是很明顯的: 4 種,就是題?中提到的四個按鍵, 分別是 A 、 C-A 、 C-C 、 C-V ( Ctrl 簡寫為 C ) 。

接下來, 思考?下對于這個問題有哪些「狀態」 ? 或者換句話說, 我們需要
知道什么信息, 才能將原問題分解為規模更?的?問題?

你看我這樣定義三個狀態?不?: 第?個狀態是剩余的按鍵次數, ? n 表?; 第?個狀態是當前屏幕上字符 A 的數量, ? a_num 表?; 第三個狀態是剪切板中字符 A 的數量, ? copy 表?

如此定義「狀態」 , 就可以知道 base case: 當剩余次數 n 為 0 時, a_num
就是我們想要的答案

結合剛才說的 4 種「選擇」 , 我們可以把這?種選擇通過狀態轉移表?出來:

dp(n - 1, a_num + 1, copy), # A 解釋: 按下 A 鍵, 屏幕上加?個字符 同時消耗 1 個操作數dp(n - 1, a_num + copy, copy), # C-V 解釋: 按下 C-V 粘貼, 剪切板中的字符加?屏幕 同時消耗 1 個操作數dp(n - 2, a_num, a_num) # C-A C-C 解釋: 全選和復制必然是聯合使?的, 剪切板中 A 的數量變為屏幕上 A 的數量 同時消耗 2 個操作數

這樣可以看到問題的規模 n 在不斷減?, 肯定可以到達 n = 0 的 base case, 所以這個思路是正確的:

def maxA(N: int) -> int:# 對于 (n, a_num, copy) 這個狀態,# 屏幕上能最終最多能有 dp(n, a_num, copy) 個 Adef dp(n, a_num, copy):# base caseif n <= 0: return a_num;# ?種選擇全試?遍, 選擇最?的結果return max(dp(n - 1, a_num + 1, copy), # Adp(n - 1, a_num + copy, copy), # C-Vdp(n - 2, a_num, a_num) # C-A C-C)# 可以按 N 次按鍵, 屏幕和剪切板?都還沒有 Areturn dp(N, 0, 0)

這個解法應該很好理解, 因為語義明確。 下?就繼續?流程, ?備忘錄消除?下重疊?問題:

def maxA(N: int) -> int:# 備忘錄memo = dict()def dp(n, a_num, copy):if n <= 0: return a_num;# 避免計算重疊?問題if (n, a_num, copy) in memo:return memo[(n, a_num, copy)]memo[(n, a_num, copy)] = max(# ?種選擇還是?樣的)return memo[(n, a_num, copy)]return dp(N, 0, 0)

這個算法的時間復雜度不容易分析。 我們可以把這個 dp 函數寫成 dp 數組:

dp[n][a_num][copy] # 狀態的總數(時空復雜度) 就是這個三維數組的體積

我們知道變量 n 最多為 N , 但是 a_num 和 copy 最多為多少我們很難計算, 復雜度起碼也有 O(N^3) 把。 所以這個算法并不好, 復雜度太?, 且已經?法優化了。
這也就說明, 我們這樣定義「狀態」 是不太優秀的, 下?我們換?種定義dp 的思路。

第?種思路

繼續?流程, 「選擇」 還是那 4 個,

但是這次我們只定義?個「狀態」 , 也就是剩余的敲擊次數 n。

這個算法基于這樣?個事實, 最優按鍵序列?定只有兩種情況:

要么?直按 A : A,A,…A(當 N ?較?時)
要么是這么?個形式: A,A,…C-A,C-C,C-V,C-V,…C-V(當 N ?較?時)

因為字符數量少(N ?較?) 時, C-A C-C C-V 這?套操作的代價相對?較?, 可能不如?個個按 A ; ?當 N ?較?時, 后期 C-V 的收獲肯定很?。

這種情況下整個操作序列?致是: 開頭連按?個 A , 然后 C-A C-C組合再接若? C-V , 然后再 C-A C-C 接著若? C-V , 循環下去。

換句話說, 最后?次按鍵要么是 A 要么是 C-V 。 明確了這?點, 可以通過這兩種情況來設計算法:

int[] dp = new int[N + 1]; // 定義: dp[i] 表? i 次操作后最多能顯?多少個 A for (int i = 0; i <= N; i++)dp[i] = max(這次按 A 鍵,這次按 C-V)

對于「按 A 鍵」 這種情況, 就是狀態 i - 1 的屏幕上新增了?個 A ?已, 很容易得到結果:

// 按 A 鍵, 就?上次多?個 A ?已 dp[i] = dp[i - 1] + 1;

但是, 如果要按 C-V , 還要考慮之前是在哪? C-A C-C 的。

剛才說了, 最優的操作序列?定是 C-A C-C 接著若? C-V , 所以我們??
個變量 j 作為若? C-V 的起點。 那么 j 之前的 2 個操作就應該是 C-AC-C
了:

public int maxA(int N) {int[] dp = new int[N + 1];dp[0] = 0;for (int i = 1; i <= N; i++) {// 按 A 鍵dp[i] = dp[i - 1] + 1;for (int j = 2; j < i; j++) {// 全選 & 復制 dp[j-2], 連續粘貼 i - j 次// 屏幕上共 dp[j - 2] * (i - j + 1) 個 Adp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 1));}} // N 次按鍵之后最多有?個 A?return dp[N]; }

其中 j 變量減 2 是給 C-A C-C 留下操作數, 看個圖就明?了:

此算法就完成了, 時間復雜度 O(N2)O(N^2)O(N2), 空間復雜度 O(N)O(N)O(N), 這種解法應該是?較?效的了

最后總結

動態規劃難就難在尋找狀態轉移, 不同的定義可以產?不同的狀態轉移邏輯, 雖然最后都能得到正確的結果, 但是效率可能有巨?的差異。

回顧第?種解法, 重疊?問題已經消除了, 但是效率還是低, 到底低在哪?呢? 抽象出遞歸框架:

def dp(n, a_num, copy):dp(n - 1, a_num + 1, copy), # Adp(n - 1, a_num + copy, copy), # C-Vdp(n - 2, a_num, a_num) # C-A C-C

看這個窮舉邏輯, 是有可能出現這樣的操作序列 C-A C-C, C-A C-C... 或者C-V,C-V,... 。 然這種操作序列的結果不是最優的, 但是我們并沒有想辦法規避這些情況的發?, 從?增加了很多沒必要的?問題計算

回顧第?種解法, 我們稍加思考就能想到, 最優的序列應該是這種形式: A,A…C-A,C-C,C-V,C-V…C-A,C-C,C-V… 。

根據這個事實, 我們重新定義了狀態, 重新尋找了狀態轉移, 從邏輯上減少了?效的?問題個數, 從?提?了算法的效率

超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

總結

以上是生活随笔為你收集整理的动态规划之四键键盘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。