題意
給定一個英文單詞,用兩隻手指去碼字。每隻手指移動所需要的代價爲鍵盤上兩字母之間的距離,求碼完整個單詞所需要的最小代價。
解法
最直觀的想法是,運用來表示輸入到第個字母,並且第一隻手指在,第二隻手指在時的最小代價。這時狀態轉移方程爲:
- 移動左手:
- 移動右手:
由於每個狀態都是從上個狀態轉移過來的,通過優化,我們可以把第一維度優化掉,變成這個形式。這種方法的時間複雜度沒變,爲。
仔細觀察後,我們發現區分左右手的其實是沒有必要的,比如和是一樣的,因爲我們可以想象隨時左右手交換,結果也不會改變。同時,一個更重要的發現是,由於我們是按順序碼字,因此每次總有一隻手停留在上一個剛碼字母的那個字母處。
有了這個信息,我們可以只記錄後手所在的位置(因爲先手一定落在剛剛敲擊完字母的那個位置),來進行動態規劃。我們用表示先手在剛剛敲擊完,後手停留在字母處的最小代價。於是狀態轉移方程變爲:
- 移動先手:
- 移動後手:
這樣一來,時間複雜度便可優化到。同理,我們可以優化掉第一維空間,使得最終的空間複雜度爲常數級別。
思考
對於此類題型,我們如果發現以下兩個特徵:
- 多維DP數組填不滿或者有冗餘(左右順序交換不太重要,比如等同於)
- 多維DP數組裏不同維度的值加起來等於一個恆定值(比如)
這種情況,可以去想着通過優化空間得到更優解。
代碼
const int INF = 0x3f3f3f3f;
class Solution {
public:
int dp[2][27];
vector<vector<int>> cost;
pair<int, int> getPos(int x) {
return make_pair(x/6, x%6);
}
int calculateDis(pair<int, int> pos1, pair<int, int> pos2) {
return abs(pos1.first-pos2.first) + abs(pos1.second-pos2.second);
}
int minimumDistance(string word) {
cost = vector<vector<int>>(30, vector<int>(30, 0));
for (int i=0; i<26; i++)
for (int j=0; j<=i; j++) {
pair<int, int> pos1 = getPos(i);
pair<int, int> pos2 = getPos(j);
cost[i][j] = cost[j][i] = calculateDis(pos1, pos2);
}
memset(dp, 0x3f, sizeof(dp));
for (int i=0; i<26; i++)
dp[0][i] = 0;
int len = word.length();
for (int idx=1; idx<len; idx++) {
int cur = idx&1, prev = !cur;
int a1 = word[idx-1] - 'A', a2 = word[idx] - 'A';
for (int t=0; t<26; t++) if (dp[prev][t] != INF) {
dp[cur][t] = min(dp[cur][t], dp[prev][t] + cost[a1][a2]);
dp[cur][a1] = min(dp[cur][a1], dp[prev][t] + cost[t][a2]);
}
for (int i=0; i<26; i++)
dp[prev][i] = INF;
}
int ans = INF;
for (int i=0; i<26; i++)
ans = min(ans, dp[(len-1)&1][i]);
return ans;
}
};
相關題型
題目:
給定一個類似雙端隊列的數組,每次取元素只能從兩頭取,但是讀此數組中的元素可以讀任意位置上的。再給定一個數,以及長度爲K的數組,要求從裏取個元素,與這個的元素從左到右依次相乘,求乘積和最大爲多少?
思路:
這個題還是可以開一個的數組,代表總共取了step個數字,其中數組左邊取了個元素,數組右邊取了個元素時的最優乘積。
然而,注意到我們可以利用類似上個題空間優化的思想,用dp[step][x]表示 目前已經取了個數字,並且這個數字裏包含左端個數字且上一個數字取的是左手邊第x個數字的最優乘積。得到以下狀態轉移方程:
接着同理,優化掉第一維度數組,自底向上DP求解即可。這裏時間複雜度沒變,爲,但空間複雜度優化到了。
參考文獻
https://leetcode.com/problems/minimum-distance-to-type-a-word-using-two-fingers/discuss/477652/JavaC%2B%2BPython-1D-DP-O(1)-Space