標籤:

初探動態規劃(一)

近來把leetcode上關於動態規劃的題目整理了一下。//抱著重新溫習的想法梳理一遍。

之前一直覺得動態規劃只要找到動態轉移方程就行了,後來發現還是有許多坑。 Orz

p.s. 本文題目選自leetcode P474以及 P494

問題 一

在計算機世界中,我們通常使用最少的資源去獲取最大的利益。

現在,假設你擁有m個0和n個1。同時,這裡有一組由0,1隨機組合組成的序列。

你的任務是用你所擁有的0,1資源去匹配最大可能符合的所有字元串。每一個0,1序列最多只能匹配一次,擁有的0,1數也只能用一次。

注意:

1. 給定0,1的數量不會超過100

2. 給定的序列不會超過600

例1

輸入: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3

輸出: 4

解釋:我們可以用擁有的5個『0』和3個『1』分別組成「10」, 「0001」, 「1」, 「0」.

例2

輸入: Array = {"10", "0", "1"}, m = 1, n = 1

輸出: 2

解釋: 儘管我們可以組成「10」, 但是這樣並不能獲得最大數量,一個更好的策略是,組成"0", "1".

解析

此題類似於背包問題。我們可以對該問題稍微做一個變型:將給定的字元串數組,根據其每個字元串中含有0,1的個數,用表示0,1個數的數量去代表他。(這裡並不需要考慮在某一個字元串中0,1的排列順序。)

即把輸入格式從

vector<string>& strs --> vector<pair<int, int>> pairs.

這樣,原問題就轉化為

給定 m 個0和 n 個1,現有一系列物品,每個物品需要 m_i 個0和 n_i 個1,求能夠獲得最大物品的數量。

因此,我們定義一個二維數組 dp(i, j) 代表當我們有i個0,j個1時,最多能夠獲得多少物品。

則狀態轉移方程: dp(i, j) = max(dp(i, j), dp(i-m_p, j-n_p) + 1) 即我們取在放入當前 所需m_p 個1與 n_p 個0前能夠獲得最多的那一組。

參考代碼

問題 二

給定一組非負整數序列 a_1, a_2, a_3, a_4...a_n 以及一個目標值 S . 現在,我們有兩種符號"+", "-".對於每一個整數,你可以選擇對其施展」+「或」-"操作作為它的前綴。

我們需要找出一共有多少種符號賦予的方法,使得這一組非負整數通過這些符號,得出的運算結果等於目標值 S

注意:

1. 非負整數的長度不會超過20。

2. 非負整數序列的和不會超過1000。

3. 輸出的結果在大小4位元組以內。

例1

輸入: nums is [1, 1, 1, 1, 1], S is 3.

輸出: 5

解釋:

-1+1+1+1+1 = 3

+1-1+1+1+1 = 3

+1+1-1+1+1 = 3

+1+1+1-1+1 = 3

+1+1+1+1-1 = 3

即,總共有5種方法使得運算結果等於目標值3.

解析

最容易想到的方法是回溯法。但是考慮到最長長度為20,極限情況下會將嘗試 2^{30} 次種可能性。一般leet不給過。於是考慮dp方法解決。

同樣的,將該問題進行適當的轉化:問題其實將原序列(集合)劃分為一組正集合和一組負組合(由被賦予「-"的數組成),因此:

S_{positive} + S_{negative} = target (1)

S_{positive} - S_{negative} = sum_{i=1}^{n}nums[i] (2)

兩式相減, 有:

2*S_{positive} = target + sum_{i=1}^{n}nums[i] (3)

也就是

S_{positive} = frac{target + sum_{i=1}^{n}nums[i]}{2} (4)

其中,(4) 式右側皆是常量。 因此問題轉化為:

已知集合 S求所有和等於 S_{positive} 的子集的數量。 也就是subset sum問題。

因此,我們定義一個一維數組 dp[i] ,代表目標值為i時,所有滿足條件的子集數量。

其狀態轉移方程: dp[i] = sum{dp[i-nums[j]]}

參考代碼

有一些比較細節的地方, 在考慮給定數組元素是否可重用上,會導致部分遍歷方式不一樣。 很有意思 :)


推薦閱讀:

初探動態規劃(三)
HEXO+Github搭建個人博客
從零開始搭建個人博客站

TAG:個人博客 |