1. 程式人生 > >高效面試之動態規劃DP

高效面試之動態規劃DP

解題關鍵:

理解結構特徵,抽象出狀態,寫成狀態轉移方程。

題目索引

1.三角形找一條從頂到底的最小路徑

分析

設狀態為 f (i; j ),表示從從位置 (i; j ) 出發,路徑的最小和,則狀態轉移方程為

f(i,j)=min{f(i+1,j),f(i+1,j+1)}+(i,j)

2.最大子陣列和

設狀態為 f[j],表示以 S[j] 結尾的最大連續子序列和,狀態轉移方程如下:

f=max(f+A[i],A[i]);//對於數組裡的一個整數,它只有兩種 選擇:1、加入之前的 SubArray2. 自己另起一個 SubArray

maxsum=max(maxsum,f);// 

求字串中最大的

3.迴文最小劃分次數

對輸入的字串劃分為一組迴文字串,最小的分割次數

所以要轉換成一維 DP。如果每次,從 i 往右掃描,每找到一個迴文就算一次 DP 的話,就可以

轉換為 f(i)= 區間 [i, n-1] 之間最小的 cut 數,為字串長度,則狀態轉移方程為

 

4.最佳時間買賣股票

設狀態f(i)表示區間[0,i-1]上的最大利潤,設定狀態g(i),表示區間[i,n-1]上最大利潤。

則最大利潤為max{f(i)+g(i)};允許在一天內買進又賣出,相當於不交易,因為題目的規定是最多兩次,而不是一定要兩次

5.判斷字串s3是否由s1,s2交叉存取組成

設狀態 f[i][j],表示 s1[0,i]  s2[0,j],匹配 s3[0, i+j]。如果 s1 的最後一個字元等 s3 的最後一個字元,則

 f[i][j]=f[i-1][j]

如果 s2 的最後一個字元等於 s3 的最後一個字元,

f[i][j]=f[i][j-1]

因此狀態轉移方程如下:

f[i][j] = (s1[i - 1] == s3 [i + j - 1] && f[i - 1][j]) || (s2[j - 1] == s3 [i + j - 1] && f[i][j - 1]);

6.給定一個矩形表格,求從頂到底的最小和

Minimum Path Sum

設狀態為 f[i][j],表示從起點 (0; 0) 到達 (i; j ) 的最小路徑和,則狀態轉移方程為:

f[i][j]=min(f[i-1][j], f[i][j-1])+grid[i][j]

7.使兩個字串相等,最小的編輯次數

Edit Distance

設狀態為 f[i][j],表示 A[0,i]  B[0,j] 之間的最小編輯距離。設 A[0,i] 的形式是

str1cB[0,j] 的形式是 str2d

1. 如果 c==d,則 f[i][j]=f[i-1][j-1]

2. 如果 c!=d

(a) 如果將 c 替換成 d,則 f[i][j]=f[i-1][j-1]+1

(b) 如果在 c 後面新增一個 d,則 f[i][j]=f[i][j-1]+1

(c) 如果將 c 刪除,則 f[i][j]=f[i-1][j]+1

8.給定一串數字,1對應A2對應B,26對應Z,求有多少種解碼方式

Decode Ways

和爬樓梯問題一樣,

 f (n) 表示爬 n 階樓梯的不同方法數,為了爬到第 n 階樓梯,有兩個選擇:

• 從第 n-1 階前進 1 步;

• 從第 n-2 階前進 2 步;

因此,有 f (n) = f (n-1) + f (n-2)這是一個斐波那契數列。

9.不同的子序列Distinct Subsequences

給定2個字串a, b,求ba中出現的次數。要求可以是不連續的,但是ba中的順序必須和b以前的一致。

Here is an example: S = "rabbbit", T = "rabbit"

Return 3.

類似於數字分解的題目。dp[i][j]表示:b的前j個字元在a的前i個字元中出現的次數

如果S[i]==T[j],那麼dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

意思是:如果當前S[i]==T[j],那麼當前這個字母即可以保留也可以拋棄,所以變換方法等於保留這個字母的變換方法加上不用這個字母的變換方法。如果S[i]!=T[i],那麼dp[i][j] = dp[i-1][j]

意思是如果當前字元不等,那麼就只能拋棄當前這個字元

遞迴公式中用到的dp[0][0] = 1dp[i][0] = 0(把任意一個字串變換為一個空串只有一個方法

10.單詞分解Word Break

字串是否可以分解為給定的單詞

For example, given

s = "leetcode",

dict = ["leet", "code"].

dp[i]  表示源串的前i個字元可以滿足分割,那麼 dp[ j ] 滿足分割的條件是存在使得 dp [k] && substr[k,j]在字典裡。

真題:

1.Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

分析狀態為 f (i; j ),表示從從位置 (i; j ) 出發,路徑的最小和,則狀態轉移方程為 f(i,j)=min{f(i+1,j),f(i+1,j+1)}+(i,j) 程式碼 // LeetCode, Triangle // 時間複雜度 O(n^2),空間複雜度 O(1) class Solution { public: int minimumTotal (vector<vector<int>>& triangle)  {     for (int i = triangle.size() - 2; i >= 0; --i)     {         for (int j = 0; j < i + 1; ++j)             triangle[i][j] += min(triangle[i + 1][j], triangle[i + 1][j + 1]);      }     return triangle [0][0];  }; 2.Maximum Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.

自己最初想法: 需找最大字串的起點和終點。不是特別好解 分析: 答案:把原字串分成很多不同的字串,然後求出字串中最大的
把原字串分成很多不同的字串,通過max(f+A[i],A[i])就可以搞定,如果之前的對我沒貢獻,還不如另起一個字串

狀態為 f[j],表示以 S[j] 結尾的最大連續子序列和,狀態轉移方程如下:

f=max(f+A[i],A[i]);//對於數組裡的一個整數,它只有兩種 選擇:1、加入之前的 SubArray;2. 自己另起一個 SubArray。

maxsum=max(maxsum,f);// 求字串中最大的

程式碼:

class Solution {

public:

    int maxSubArray(int A[], int n) {

        if(0==n) return 0;

        int f=0;//f[j],表示以 A[j] 結尾的最大連續子序列和

        int maxsum=A[0];

        for(int i=0;i<n;++i)

        {

            f=max(f+A[i],A[i]);//是否需要另起一個字串,如果之前的對我沒貢獻,還不如另起一個字串。

            maxsum=max(maxsum,f); //字串中最大的

        }

        return maxsum;

    }

};

3.Palindrome Partitioning II

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

題意分析: 對輸入的字串劃分為一組迴文字串,最小的分割次數

分析

定義狀態 f(i,j) 表示區間 [i,j] 之間最小的 cut 數,則狀態轉移方程為

這是一個二維函式,實際寫程式碼比較麻煩。

所以要轉換成一維 DP。如果每次,從 i 往右掃描,每找到一個迴文就算一次 DP 的話,就可以

轉換為 f(i)= 區間 [i, n-1] 之間最小的 cut 數,n 為字串長度,則狀態轉移方程為

一個問題出現了,就是如何判斷 [i,j] 是否是迴文?每次都從 i 到 j 比較一遍?太浪費了,這 裡也是一個 DP 問題。 

定義狀態 P[i][j] = true if [i,j] 為迴文,那麼

P[i][j] = str[i] == str[j] && P[i+1][j-1]

程式碼

// LeetCode, Palindrome Partitioning II

// 時間複雜度 O(n^2),空間複雜度 O(n^2)

class Solution {

public:

  int minCut(string s)

 {

    const int n = s.size();

    int f[n+1];

    bool p[n][n];

    fill_n(&p[0][0], n * n, false);

//the worst case is cutting by each char

    for (int i = 0; i <= n; i++)

            f[i] = n - 1 - i; // 最後一個 f[n]=-1

    for (int i = n - 1; i >= 0; i--)

    {

        for (int j = i; j < n; j++)

         {

            if (s[i] == s[j] && (j - i < 2 || p[i + 1][j - 1])) 

            { 

                    p[i][j] = true;

                    f[i] = min(f[i], f[j + 1] + 1);

           }

        }

    }

    return f[0];

}

}

4.Maximal Rectangle

描述

Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle containing all ones and return

its area.

題目就是給一個矩陣,找一個全是一的最大子矩陣。

5.Best Time to Buy and Sell Stock III

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

分析:

設狀態f(i)表示區間[0,i-1]上的最大利潤,設定狀態g(i),表示區間[i,n-1]上最大利潤。

則最大利潤為max{f(i)+g(i)};允許在一天內買進又賣出,相當於不交易,因為題目的規定是最多兩次,而不是一定要兩次。

程式碼

// LeetCode, Best Time to Buy and Sell Stock III

// 時間複雜度 O(n),空間複雜度 O(n)

class Solution {

public:

int maxProfit(vector<int>& prices)

 {

    if (prices.size() < 2) return 0;

    const int n = prices.size();

    vector<int> f(n, 0);

    vector<int> g(n, 0);

    for (int i = 1, valley = prices[0]; i < n; ++i) {

        valley = min(valley, prices[i]);

        f[i] = max(f[i - 1], prices[i] - valley);

    }

    for (int i = n - 2, peak = prices[n - 1]; i >= 0; --i) {

        peak = max(peak, prices[i]);

        g[i] = max(g[i], peak - prices[i]);

    }

    int max_profit = 0;

    for (int i = 0; i < n; ++i)

        max_profit = max(max_profit, f[i] + g[i]);

    return max_profit;

    }

};

6.Interleaving String

Given s1s2s3, find whether s3 is formed by the interleaving of s1 and s2.

For example,
Given:
s1 = "aabcc",
s2 = "dbbca",

When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.

分析:判斷字串s3是否由s1,s2交叉存取組成

設狀態 f[i][j],表示 s1[0,i] 和 s2[0,j],匹配 s3[0, i+j]。如果 s1 的最後一個字元等 於 s3 的最後一個字元,則

 f[i][j]=f[i-1][j];

如果 s2 的最後一個字元等於 s3 的最後一個字元, 則 

f[i][j]=f[i][j-1]。

因此狀態轉移方程如下: 

f[i][j] = (s1[i - 1] == s3 [i + j - 1] && f[i - 1][j]) || (s2[j - 1] == s3 [i + j - 1] && f[i][j - 1]);

1 class Solution {
 2 private:
 3     bool f[1000][1000];
 4 public:
 5     bool isInterleave(string s1, string s2, string s3) {
 6         // Start typing your C/C++ solution below
 7         // DO NOT write int main() function 
 8         if (s1.size() + s2.size() != s3.size())
 9             return false;
10             
11         f[0][0] = true;
12         for(int i = 1; i <= s1.size(); i++)
13             f[i][0] = f[i-1][0] && (s3[i-1] == s1[i-1]);
14             
15         for(int j = 1; j <= s2.size(); j++)
16             f[0][j] = f[0][j-1] && (s3[j-1] == s2[j-1]);
17             
18         for(int i = 1; i <= s1.size(); i++)
19             for(int j = 1; j <= s2.size(); j++)
20                 f[i][j] = (f[i][j-1] && s2[j-1] == s3[i+j-1]) || (f[i-1][j] && s1[i-1] == s3[i+j-1]);
21                 
22         return f[s1.size()][s2.size()];
23     }
24 };

動規 + 滾動陣列

// LeetCode, Interleaving String

// 二維動規 + 滾動陣列,時間複雜度 O(n^2),空間複雜度 O(n)

class Solution {

public:

bool isInterleave(string s1, string s2, string s3)

 {

    if (s1.length() + s2.length() != s3.length())

        return false;

    if (s1.length() < s2.length())

        return isInterleave(s2, s1, s3);

    vector<bool> f(s2.length() + 1, true);

    for (size_t i = 1; i <= s2.length(); ++i)

        f[i] = s2[i - 1] == s3[i - 1] && f[i - 1];

    for (size_t i = 1; i <= s1.length(); ++i)

     {

        f[0] = s1[i - 1] == s3[i - 1] && f[0];

        for (size_t j = 1; j <= s2.length(); ++j)

            f[j] = (s1[i - 1] == s3[i + j - 1] && f[j]) || (s2[j - 1] == s3[i + j - 1] && f[j - 1]); 

    }

    return f[s2.length()];

}

};

7.Scramble String(混雜字串)

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.

Below is one possible representation of s1 = "great":

    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t

To scramble the string, we may choose any 

相關推薦

高效面試動態規劃DP

解題關鍵: 理解結構特徵,抽象出狀態,寫成狀態轉移方程。 題目索引 1.三角形找一條從頂到底的最小路徑 分析 設狀態為 f (i; j ),表示從從位置 (i; j ) 出發,路徑的最小和,則狀態轉移方程為 f(i,j)=min{f

立體匹配動態規劃DP

動態規劃通常應用於最優化問題的求解中,Baker、Ohta 等將動態規劃引入立體匹配中來獲取視差圖。動態規劃匹配的過程可以分為兩個階段,建立視差空間和動態規劃優化,將立體匹配問題轉化為視差空間內尋找最優路徑的問題。       &nbs

由Leetcode詳解演算法 動態規劃DP

因為最近一段時間接觸了一些Leetcode上的題目,發現許多題目的解題思路相似,從中其實可以瞭解某類演算法的一些應用場景。 這個隨筆系列就是我嘗試的分析總結,希望也能給大家一些啟發。 動態規劃的基本概念 一言以蔽之,動態規劃就是將大問題分成小問題,以迭代的方式求解。 可以使用動態規劃求解的問題

演算法總結動態規劃DP

適用動態規劃的特點 所解決的問題是最優化問題。 所解決的問題具有“最優子結構”。可以建立一個遞推關係,使得n階段的問題,可以通過幾個k<n階段的低階子問題的最優解來求解。 具有“重疊子結構”的特點。即,求解低階子問題時存在重複計算。 詞典法 大家都知道,遞迴演算法一般都存在大量的重複計算,這會造成不

演算法動態規劃DP

前言 前言_ 我們遇到的問題中,有很大一部分可以用動態規劃(簡稱DP)來解。 解決這類問題可以很大地提升你的能力與技巧,我會試著幫助你理解如何使用DP來解題。 這篇文章是基於例項展開來講的,因為乾巴巴的理論實在不好理解。 注意:如果你對於其中某一節已經瞭解並且不

【演算法動態規劃(一)】動態規劃DP)詳解

一、基本概念 動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep d

狀態壓縮與動態規劃(DP)---程式設計美---瓷磚覆蓋地板---POJ2411

一、狀態壓縮 從狀態壓縮的特點來看,這個演算法適用的題目符合以下的條件: 1.解法需要儲存一定的狀態資料(表示一種狀態的一個數據值) ,每個狀態資料通常情況下是可以通過 2 進位制來表示的。這就要求狀態資料的每個單元只有兩種狀態,比如說棋盤上的格子,放棋子或者不放,或者是硬

算法整理動態規劃

getc 程序 span bool hid 沒有 知識 else 多好 我現在介紹的這個版本,是從算法愛好者中看到的一個別人的漫畫版本。題目:有一座高度是10級臺階的樓梯,從下往上走,每跨一步只能向上1級或者2級臺階。要求用程序來求出一共有多少種走法。比如,每次走1級臺階,

動態規劃 DP

code tchar getch spa div 發現 style logs print 動態規劃 DP 我們用f[ i ] 表示從 i 點出發到達終點的最多能休息的時間 然後我們發現 狀態轉移方程f[ i ] = f[ i+1 ] +1 ; 當

NOI題庫 / 2.6基本算法動態規劃 - 8471:切割回文

多少 ++i turn aac als return 得到 什麽 包含 總時間限制: 1000ms 內存限制: 65536kB描述 阿福最近對回文串產生了非常濃厚的興趣。 如果一個字符串從左往右看和從右往左看完全相同的話,那麽就認為這個串是一個回文串。例如,“abcaacb

五大經典算法動態規劃

變更 strong com buying == exp int done 索引 一、概念起源 ??動態規劃,又名DP算法(取自其Dynamic Programming的縮寫),最初是運籌學的一個分支,是用來求解決策過程最優化的數學方法。 二、基本思想 ??把 多階段過程 轉

動態規劃dp

style put string can ive 我們 nta contain poi 題目:http://poj.org/problem?id=1837 Balance Time Limit: 1000MS Memory Limit: 30000K Tot

2018 浙江省大學生程式設計競賽 D.Sequence Swapping(動態規劃dp,思維邏輯)

轉載出處:https://blog.csdn.net/i11000/article/details/80209662 #include<cstdio> #include<cstring> #include<iostream> #include<queue

LeetCode :騰訊秋招動態規劃

https://leetcode-cn.com/explore/interview/card/tencent/226/dynamic-programming/  爬樓梯 假設你正在爬樓梯。需要 n 階你才能到達樓頂。 每次你可以爬 1 或 2 個臺階。你有多少種不

【演算法】動態規劃

含義     動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。 分類     動態規劃一般可分為線性動規,

POJ-2184 Cow Exhibition 【動態規劃DP+01揹包變換】

題目傳送門 題目:共有N頭牛,接下來N行是每頭牛的智商和情商,從這些牛中任意選取若干頭牛,使得牛的智商和+情商和最大,同時智商和(TS),情商和(TF)都不小於0。 題解:以智商作為容量,求前i頭牛在智商為j的情況下的最大情商 。因為有負數,所以容量擴大100000,修改dp陣

HDU-2844 Coins 【動態規劃DP+多重揹包】

題目傳送門 題目:有n種硬幣,第i種硬幣的價值為Ai,數目為Ci,求這些硬幣能配出1~m中的幾種價值。 題解:dp[j]表示是否能配出價值j。sum[i][j]表示第i種硬幣取到價值j時需要的數目。sum陣列可以壓掉i的那一維,每次都要記得清零。 AC程式碼: #include

POJ-3666 Making the Grade 【動態規劃DP+滾動陣列】

題目傳送門 題目:輸入n個數,第i個數字的值為a[i],把第i個數變為j的代價為a[i]-j的絕對值,求把這n個數組成的數列變成單調數列的最小代價。 題解:dp[i][j]表示前i個數最大值為b[j]時的最小代價,即第i個數在總數列中的值為第j小的時候的最小代價。 動態轉移方程:dp

POJ-2392 Space Elevator 【動態規劃DP+多重揹包】

題目傳送門 題目:牛要去太空了!他們計劃通過建造一種太空升降機來達到軌道:一個巨大的積木塔。他們有K (1 <= K <= 400)不同型別的積木來建造塔。型別i的每個塊的高度都是h_i (1 <= h_i <= 100),並且數量上都是c_i (1 <= c_

C++ Leetcode初級演算法動態規劃

1.爬樓梯 假設你正在爬樓梯。需要 n 階你才能到達樓頂。 每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢? 注意:給定 n 是一個正整數。 示例 1: 輸入: 2 輸出: 2 解釋: 有兩種方法可以爬到樓頂。 1.1 階 + 1 階 2.2 階 示