久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

動(dòng)態(tài)規(guī)劃DP問(wèn)題分類和經(jīng)典題型

 dinghj 2019-09-22

解題關(guān)鍵:

解結(jié)構(gòu)特征,,抽象出狀態(tài),寫(xiě)成狀態(tài)轉(zhuǎn)移方程。

動(dòng)態(tài)規(guī)劃理念:

1.最優(yōu)化原理
   1951年美國(guó)數(shù)學(xué)家R.Bellman等人,,根據(jù)一類多階段問(wèn)題的特點(diǎn),,把多階段決策問(wèn)題變換為一系列互相聯(lián)系的單階段問(wèn)題,,然后逐個(gè)加以解決,。一些靜態(tài)模型,只要人為地引進(jìn)“時(shí)間”因素,,分成時(shí)段,,就可以轉(zhuǎn)化成多階段的動(dòng)態(tài)模型,用動(dòng)態(tài)規(guī)劃方法去處理,。與此同時(shí),,他提出了解決這類問(wèn)題的“最優(yōu)化原理”(Principle of optimality):
    “一個(gè)過(guò)程的最優(yōu)決策具有這樣的性質(zhì):即無(wú)論其初始狀態(tài)和初始決策如何,其今后諸策略對(duì)以第一個(gè)決策所形成的狀態(tài)作為初始狀態(tài)的過(guò)程而言,,必須構(gòu)成最優(yōu)策略”,。簡(jiǎn)言之,一個(gè)最優(yōu)策略的子策略,,對(duì)于它的初態(tài)和終態(tài)而言也必是最優(yōu)的,。
    這個(gè)“最優(yōu)化原理”如果用數(shù)學(xué)化一點(diǎn)的語(yǔ)言來(lái)描述的話,就是:假設(shè)為了解決某一優(yōu)化問(wèn)題,,需要依次作出n個(gè)決策D1,,D2,,…,Dn,,如若這個(gè)決策序列是最優(yōu)的,,對(duì)于任何一個(gè)整數(shù)k,,1 < k < n,,不論前面k個(gè)決策是怎樣的,,以后的最優(yōu)決策只取決于由前面決策所確定的當(dāng)前狀態(tài),即以后的決策Dk+1,,Dk+2,,…,Dn也是最優(yōu)的,。
    最優(yōu)化原理是動(dòng)態(tài)規(guī)劃的基礎(chǔ),。任何一個(gè)問(wèn)題,,如果失去了這個(gè)最優(yōu)化原理的支持,,就不可能用動(dòng)態(tài)規(guī)劃方法計(jì)算。能采用動(dòng)態(tài)規(guī)劃求解的問(wèn)題都需要滿足一定的條件: 
    (1) 問(wèn)題中的狀態(tài)必須滿足最優(yōu)化原理,;
    (2) 問(wèn)題中的狀態(tài)必須滿足無(wú)后效性,。
    所謂的無(wú)后效性是指:“下一時(shí)刻的狀態(tài)只與當(dāng)前狀態(tài)有關(guān),而和當(dāng)前狀態(tài)之前的狀態(tài)無(wú)關(guān),,當(dāng)前的狀態(tài)是對(duì)以往決策的總結(jié)”,。

2.問(wèn)題求解模式 
    動(dòng)態(tài)規(guī)劃所處理的問(wèn)題是一個(gè)多階段決策問(wèn)題,一般由初始狀態(tài)開(kāi)始,,通過(guò)對(duì)中間階段決策的選擇,,達(dá)到結(jié)束狀態(tài)。這些決策形成了一個(gè)決策序列,,同時(shí)確定了完成整個(gè)過(guò)程的一條活動(dòng)路線(通常是求最優(yōu)的活動(dòng)路線),。如圖所示。動(dòng)態(tài)規(guī)劃的設(shè)計(jì)都有著一定的模式,,一般要經(jīng)歷以下幾個(gè)步驟,。

   初始狀態(tài)→│決策1│→│決策2│→…→│決策n│→結(jié)束狀態(tài)
     圖1 動(dòng)態(tài)規(guī)劃決策過(guò)程示意圖

    (1)劃分階段:按照問(wèn)題的時(shí)間或空間特征,把問(wèn)題分為若干個(gè)階段,。在劃分階段時(shí),,注意劃分后的階段一定要是有序的或者是可排序的,否則問(wèn)題就無(wú)法求解,。
    (2)確定狀態(tài)和狀態(tài)變量:將問(wèn)題發(fā)展到各個(gè)階段時(shí)所處于的各種客觀情況用不同的狀態(tài)表示出來(lái),。當(dāng)然,狀態(tài)的選擇要滿足無(wú)后效性,。
    (3)確定決策并寫(xiě)出狀態(tài)轉(zhuǎn)移方程:因?yàn)闆Q策和狀態(tài)轉(zhuǎn)移有著天然的聯(lián)系,,狀態(tài)轉(zhuǎn)移就是根據(jù)上一階段的狀態(tài)和決策來(lái)導(dǎo)出本階段的狀態(tài)。所以如果確定了決策,,狀態(tài)轉(zhuǎn)移方程也就可寫(xiě)出,。但事實(shí)上常常是反過(guò)來(lái)做,根據(jù)相鄰兩段各狀態(tài)之間的關(guān)系來(lái)確定決策。
    (4)尋找邊界條件:給出的狀態(tài)轉(zhuǎn)移方程是一個(gè)遞推式,,需要一個(gè)遞推的終止條件或邊界條件,。

3.實(shí)現(xiàn)
    動(dòng)態(tài)規(guī)劃的主要難點(diǎn)在于理論上的設(shè)計(jì),也就是上面4個(gè)步驟的確定,,一旦設(shè)計(jì)完成,,實(shí)現(xiàn)部分就會(huì)非常簡(jiǎn)單。使用動(dòng)態(tài)規(guī)劃求解問(wèn)題,,最重要的就是確定動(dòng)態(tài)規(guī)劃三要素:?jiǎn)栴}的階段,每個(gè)階段的狀態(tài)以及從前一個(gè)階段轉(zhuǎn)化到后一個(gè)階段之間的遞推關(guān)系,。遞推關(guān)系必須是從次小的問(wèn)題開(kāi)始到較大的問(wèn)題之間的轉(zhuǎn)化,從這個(gè)角度來(lái)說(shuō),,動(dòng)態(tài)規(guī)劃往往可以用遞歸程序來(lái)實(shí)現(xiàn),,不過(guò)因?yàn)檫f推可以充分利用前面保存的子問(wèn)題的解來(lái)減少重復(fù)計(jì)算,所以對(duì)于大規(guī)模問(wèn)題來(lái)說(shuō),,有遞歸不可比擬的優(yōu)勢(shì),,這也是動(dòng)態(tài)規(guī)劃算法的核心之處。確定了動(dòng)態(tài)規(guī)劃的這三要素,,整個(gè)求解過(guò)程就可以用一個(gè)最優(yōu)決策表來(lái)描述,,最優(yōu)決策表是一個(gè)二維表,其中行表示決策的階段,,列表示問(wèn)題狀態(tài),,表格需要填寫(xiě)的數(shù)據(jù)一般對(duì)應(yīng)此問(wèn)題的在某個(gè)階段某個(gè)狀態(tài)下的最優(yōu)值(如最短路徑,最長(zhǎng)公共子序列,,最大價(jià)值等),,填表的過(guò)程就是根據(jù)遞推關(guān)系,從1行1列開(kāi)始,,以行或者列優(yōu)先的順序,,依次填寫(xiě)表格,最后根據(jù)整個(gè)表格的數(shù)據(jù)通過(guò)簡(jiǎn)單的取舍或者運(yùn)算求得問(wèn)題的最優(yōu)解,。下面分別以求解最大化投資回報(bào)問(wèn)題和最長(zhǎng)公共子序列問(wèn)題為例闡述用動(dòng)態(tài)規(guī)劃算法求解問(wèn)題的一般思路,。


動(dòng)態(tài)規(guī)劃經(jīng)典例題


1.三角形找一條從頂?shù)降椎淖钚÷窂?/span>

分析

設(shè)狀態(tài)為 f (i; j ),表示從從位置 (i; j ) 出發(fā),,路徑的最小和,,則狀態(tài)轉(zhuǎn)移方程為

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

 

2.最大子數(shù)組和

設(shè)狀態(tài)為 f[j],表示以 S[j] 結(jié)尾的最大連續(xù)子序列和,,狀態(tài)轉(zhuǎn)移方程如下:

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

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

 

3.回文最小劃分次數(shù)

對(duì)輸入的字符串劃分為一組回文字符串,最小的分割次數(shù)

所以要轉(zhuǎn)換成一維 DP。如果每次,從 i 往右掃描,,每找到一個(gè)回文就算一次 DP 的話,,就可以

轉(zhuǎn)換為 f(i)= 區(qū)間 [i, n-1] 之間最小的 cut 數(shù),為字符串長(zhǎng)度,,則狀態(tài)轉(zhuǎn)移方程為

 

 

4.最佳時(shí)間買(mǎi)賣(mài)股票

設(shè)狀態(tài)f(i)表示區(qū)間[0,i-1]上的最大利潤(rùn),設(shè)置狀態(tài)g(i),,表示區(qū)間[i,n-1]上最大利潤(rùn)。

則最大利潤(rùn)為max{f(i)+g(i)};允許在一天內(nèi)買(mǎi)進(jìn)又賣(mài)出,,相當(dāng)于不交易,,因?yàn)轭}目的規(guī)定是最多兩次,而不是一定要兩次


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

設(shè)狀態(tài) f[i][j],,表示 s1[0,i]  s2[0,j],,匹配 s3[0, i+j]。如果 s1 的最后一個(gè)字符等  s3 的最后一個(gè)字符,,則

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

如果 s2 的最后一個(gè)字符等于 s3 的最后一個(gè)字符,  

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

因此狀態(tài)轉(zhuǎn)移方程如下: 

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.給定一個(gè)矩形表格,求從頂?shù)降椎淖钚『?/span>

Minimum Path Sum

設(shè)狀態(tài)為 f[i][j],,表示從起點(diǎn) (0; 0) 到達(dá) (i; j ) 的最小路徑和,,則狀態(tài)轉(zhuǎn)移方程為:

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

 

7.使兩個(gè)字符串相等,最小的編輯次數(shù)

Edit Distance

設(shè)狀態(tài)為 f[i][j],,表示 A[0,i]  B[0,j] 之間的最小編輯距離,。設(shè) 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 后面添加一個(gè) d,,則 f[i][j]=f[i][j-1]+1,;

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


8.給定一串?dāng)?shù)字,,1對(duì)應(yīng)A2對(duì)應(yīng)B,26對(duì)應(yīng)Z,,求有多少種解碼方式

 Decode Ways

和爬樓梯問(wèn)題一樣,

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

· 從第 n-1 階前進(jìn) 1 步;

· 從第 n-2 階前進(jìn) 2 步,;

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

 

9. 不同的子序列Distinct Subsequences

給定2個(gè)字符串a, b,,求ba中出現(xiàn)的次數(shù)。要求可以是不連續(xù)的,,但是ba中的順序必須和b以前的一致,。 

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

Return 3.

 

類似于數(shù)字分解的題目。dp[i][j]表示:b的前j個(gè)字符在a的前i個(gè)字符中出現(xiàn)的次數(shù),。

 

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

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

意思是如果當(dāng)前字符不等,那么就只能拋棄當(dāng)前這個(gè)字符

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

 

10.單詞分解Word Break

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

For example, given

s = "leetcode",

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

dp[i]  表示源串的前i個(gè)字符可以滿足分割,,那么 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.


分析
設(shè)狀態(tài)為 f (i; j ),,表示從從位置 (i; j ) 出發(fā),,路徑的最小和,則狀態(tài)轉(zhuǎn)移方程為
f(i,j)=min{f(i+1,j),f(i+1,j+1)}+(i,j)

代碼
// LeetCode, Triangle
// 時(shí)間復(fù)雜度 O(n^2),,空間復(fù)雜度 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.


自己最初想法:
需找最大字串的起點(diǎn)和終點(diǎn),。不是特別好解

分析:
答案:把原字符串分成很多不同的字串,然后求出字串中最大的,。

把原字符串分成很多不同的字串,,通過(guò)max(f+A[i],A[i])就可以搞定,如果之前的對(duì)我沒(méi)貢獻(xiàn),,還不如另起一個(gè)字串

設(shè)狀態(tài)為 f[j],,表示以 S[j] 結(jié)尾的最大連續(xù)子序列和,狀態(tài)轉(zhuǎn)移方程如下:

f=max(f+A[i],A[i]);//對(duì)于數(shù)組里的一個(gè)整數(shù),,它只有兩種 選擇:1,、加入之前的 SubArray;2. 自己另起一個(gè) 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] 結(jié)尾的最大連續(xù)子序列和

        int maxsum=A[0];

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

        {

         

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

            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.

題意分析: 對(duì)輸入的字符串劃分為一組回文字符串,最小的分割次數(shù)


分析

定義狀態(tài) f(i,j) 表示區(qū)間 [i,j] 之間最小的 cut 數(shù),,則狀態(tài)轉(zhuǎn)移方程為

這是一個(gè)二維函數(shù),,實(shí)際寫(xiě)代碼比較麻煩。

所以要轉(zhuǎn)換成一維 DP,。如果每次,,從 i 往右掃描,每找到一個(gè)回文就算一次 DP 的話,,就可以

轉(zhuǎn)換為 f(i)= 區(qū)間 [i, n-1] 之間最小的 cut 數(shù),,n 為字符串長(zhǎng)度,則狀態(tài)轉(zhuǎn)移方程為


一個(gè)問(wèn)題出現(xiàn)了,,就是如何判斷 [i,j] 是否是回文,?每次都從 i 到 j 比較一遍?太浪費(fèi)了,,這 里也是一個(gè) DP 問(wèn)題,。 

定義狀態(tài) P[i][j] = true if [i,j] 為回文,那么

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


代碼

// LeetCode, Palindrome Partitioning II

// 時(shí)間復(fù)雜度 O(n^2),,空間復(fù)雜度 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; // 最后一個(gè) 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.

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


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).


分析:

設(shè)狀態(tài)f(i)表示區(qū)間[0,i-1]上的最大利潤(rùn),設(shè)置狀態(tài)g(i),,表示區(qū)間[i,n-1]上最大利潤(rùn),。

則最大利潤(rùn)為max{f(i)+g(i)};允許在一天內(nèi)買(mǎi)進(jìn)又賣(mài)出,相當(dāng)于不交易,,因?yàn)轭}目的規(guī)定是最多兩次,而不是一定要兩次,。

代碼

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

// 時(shí)間復(fù)雜度 O(n),,空間復(fù)雜度 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交叉存取組成


設(shè)狀態(tài) f[i][j],表示 s1[0,i] 和 s2[0,j],,匹配 s3[0, i+j],。如果 s1 的最后一個(gè)字符等 于 s3 的最后一個(gè)字符,則

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

如果 s2 的最后一個(gè)字符等于 s3 的最后一個(gè)字符,, 則 

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

因此狀態(tài)轉(zhuǎn)移方程如下: 

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 };


動(dòng)規(guī) + 滾動(dòng)數(shù)組

// LeetCode, Interleaving String

// 二維動(dòng)規(guī) + 滾動(dòng)數(shù)組,,時(shí)間復(fù)雜度 O(n^2),,空間復(fù)雜度 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 non-leaf node and swap its two children.

For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".

    rgeat
   /      rg    eat
 / \    /  r   g  e   at
           /           a   t

We say that "rgeat" is a scrambled string of "great".

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".

    rgtae
   /      rg    tae
 / \    /  r   g  ta  e
       /       t   a

We say that "rgtae" is a scrambled string of "great".

Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.



8.Minimum Path Sum

描述

Given a m  n grid filled with non-negative numbers, find a path from top left to bottom right which

minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time

分析:

設(shè)狀態(tài)為 f[i][j],表示從起點(diǎn) (0; 0) 到達(dá) (i; j ) 的最小路徑和,,則狀態(tài)轉(zhuǎn)移方程為:

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


代碼:

備忘錄法

// LeetCode, Minimum Path Sum

// 備忘錄法

class Solution

 {

public:

int minPathSum(vector<vector<int> > &grid)

 {

    const int m = grid.size();

    const int n = grid[0].size();

    this->f = vector<vector<int> >(m, vector<int>(n, -1));

    return dfs(grid, m-1, n-1);

}

private:

vector<vector<int> > f; // 緩存

int dfs(const vector<vector<int> > &grid, int x, int y)

 {

    if (x < 0 || y < 0) return INT_MAX; // 越界,,終止條件,,注意,不是 0

    if (x == 0 && y == 0) return grid[0][0]; // 回到起點(diǎn),,收斂條件

    return min(getOrUpdate(grid, x - 1, y),

    getOrUpdate(grid, x, y - 1)) + grid[x][y];

}

int getOrUpdate(const vector<vector<int> > &grid, int x, int y) 

{

    if (x < 0 || y < 0) 

            return INT_MAX; // 越界,,注意,不是 0

    if (f[x][y] >= 0) 

            return f[x][y];

    else

         return f[x][y] = dfs(grid, x, y);

}

};

動(dòng)規(guī)

// LeetCode, Minimum Path Sum

// 二維動(dòng)規(guī)

class Solution

{

public:

int minPathSum(vector<vector<int> > &grid)

 {

    if (grid.size() == 0) return 0;

    const int m = grid.size();

    const int n = grid[0].size();

    int f[m][n];

    f[0][0] = grid[0][0];

    for (int i = 1; i < m; i++)

     {

        f[i][0] = f[i - 1][0] + grid[i][0];

    }

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

     {

        f[0][i] = f[0][i - 1] + grid[0][i];

    }

    for (int i = 1; i < m; i++) 

    {

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

         {

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

        }

    }

   return f[m - 1][n - 1];

}

};

動(dòng)規(guī) + 滾動(dòng)數(shù)組

// LeetCode, Minimum Path Sum

// 二維動(dòng)規(guī) + 滾動(dòng)數(shù)組

class Solution {

public:

int minPathSum(vector<vector<int> > &grid)

 {

        const int m = grid.size();

        const int n = grid[0].size();

        int f[n];

        fill(f, f+n, INT_MAX); // 初始值是 INT_MAX,,因?yàn)楹竺嬗昧?min 函數(shù),。

        f[0] = 0;

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

         {

            f[0] += grid[i][0];

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

            {

                    // 左邊的 f[j],表示更新后的 f[j],,與公式中的 f[i[[j] 對(duì)應(yīng)

                    // 右邊的 f[j],,表示老的 f[j],與公式中的 f[i-1][j] 對(duì)應(yīng)

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

               }

            }

    return f[n - 1];

}

};


9 Edit Distance

描述

Given two words word1 and word2, find the minimum number of steps required to convert word1 to

word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

· Insert a character

· Delete a character

· Replace a character


分析

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

str1c,B[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 后面添加一個(gè) d,,則 f[i][j]=f[i][j-1]+1,;

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

動(dòng)規(guī)

// LeetCode, Edit Distance

// 二維動(dòng)規(guī),,時(shí)間復(fù)雜度 O(n*m),空間復(fù)雜度 O(n*m)

class Solution {

public:

int minDistance(const string &word1, const string &word2) {

    const size_t n = word1.size();

    const size_t m = word2.size();

    // 長(zhǎng)度為 n 的字符串,,有 n+1 個(gè)隔板

    int f[n + 1][m + 1];

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

        f[i][0] = i;

    for (size_t j = 0; j <= m; j++)

        f[0][j] = j;

    for (size_t i = 1; i <= n; i++) {

        for (size_t j = 1; j <= m; j++) {

            if (word1[i - 1] == word2[j - 1])

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

            else {

                int mn = min(f[i - 1][j], f[i][j - 1]);

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

            }

        }

}

return f[n][m];

}

};


動(dòng)規(guī) + 滾動(dòng)數(shù)組

// LeetCode, Edit Distance

// 二維動(dòng)規(guī) + 滾動(dòng)數(shù)組

// 時(shí)間復(fù)雜度 O(n*m),,空間復(fù)雜度 O(n)

class Solution {

public:

int minDistance(const string &word1, const string &word2) {

    if (word1.length() < word2.length())

        return minDistance(word2, word1);

    int f[word2.length() + 1];

    int upper_left = 0; // 額外用一個(gè)變量記錄 f[i-1][j-1]

    for (size_t i = 0; i <= word2.size(); ++i)

        f[i] = i;

    for (size_t i = 1; i <= word1.size(); ++i) {

        upper_left = f[0];

        f[0] = i;

        for (size_t j = 1; j <= word2.size(); ++j) {

            int upper = f[j];

            if (word1[i - 1] == word2[j - 1])

                f[j] = upper_left;

            else

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

            upper_left = upper;

    }

}

return f[word2.length()];

}

};


10 Decode Ways

描述

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1

'B' -> 2

...

'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example, Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2


分析 :

和爬樓梯問(wèn)題一樣,

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

· 從第 n-1 階前進(jìn) 1 步,;

· 從第 n-2 階前進(jìn) 2 步;

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


這里也一樣,,多一些約束條件而已,。判斷兩個(gè)數(shù)字時(shí),是否小于26

代碼

// LeetCode, Decode Ways

// 動(dòng)規(guī),,時(shí)間復(fù)雜度 O(n),,空間復(fù)雜度 O(1)+滾動(dòng)數(shù)組

class Solution {

public:

int numDecodings(const string &s) {

    if (s.empty() || s[0] == '0') return 0;

    int prev = 0;//f(0)=0

    int cur = 1;//f(1)=1

    // 長(zhǎng)度為 n 的字符串,,有 n+1 個(gè)階梯

    for (size_t i = 1; i <= s.size(); ++i) {

        if (s[i-1] == '0')

             cur = 0;

        if (i < 2 || !(s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))) 

            prev = 0;

        int tmp = cur;

        cur = prev + cur;//f(i)=f(i-2)+f(i-1)

        prev = tmp;

    }

    return cur;

}

};


11 Distinct Subsequences(不同的子序列)


描述
Given a string S and a string T , count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can  be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not). 
Here is an example: S = "rabbbit", T = "rabbit"
Return 3.

給定2個(gè)字符串a(chǎn), b,求b在a中出現(xiàn)的次數(shù),。要求可以是不連續(xù)的,,但是b在a中的順序必須和b以前的一致。 

分析

類似于數(shù)字分解的題目,。dp[i][j]表示:b的前j個(gè)字符在a的前i個(gè)字符中出現(xiàn)的次數(shù),。


如果a[i]==b[j],那么dp[i][j] = dp[i-1][j-1] + dp[i-1][j],。
意思是:如果當(dāng)前a[i]==b[j],,那么當(dāng)前這個(gè)字母即可以保留也可以拋棄,所以變換方法等于保留這個(gè)字母的變換方法加上不用這個(gè)字母的變換方法,。
如果a[i]!=b[i],,那么dp[i][j] = dp[i-1][j]
意思是如果當(dāng)前字符不等,,那么就只能拋棄當(dāng)前這個(gè)字符 遞歸公式中用到的dp[0][0] = 1,,dp[i][0] = 0(把任意一個(gè)字符串變換為一個(gè)空串只有一個(gè)方法


代碼

// LeetCode, Distinct Subsequences

// 二維動(dòng)規(guī) + 滾動(dòng)數(shù)組

// 時(shí)間復(fù)雜度 O(m*n),空間復(fù)雜度 O(n)

class Solution {

public:

int numDistinct(const string &S, const string &T) {

    vector<int> f(T.size() + 1);

    f[0] = 1;

    for (int i = 0; i < S.size(); ++i) {

        for (int j = T.size() - 1; j >= 0; --j) {

        f[j + 1] += S[i] == T[j] ? f[j] : 0;

        }

    }

    return f[T.size()];

}

};


12 Word Break

描述

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated

sequence of one or more dictionary words.

For example, given

s = "leetcode",

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

Return true because "leetcode" can be segmented as "leet code".


分析:

dp[i]  表示源串的前i個(gè)字符可以滿足分割,,那么 dp[ j ] 滿足分割的條件是存在k 使得 dp [k] && substr[k,j]在字典里,。
  1. class Solution {  
  2. public:  
  3.     bool wordBreak(string s, unordered_set<string> &dict) {  
  4.         // Note: The Solution object is instantiated only once and is reused by each test case.  
  5.         int n = (int)s.size();  
  6.         vector<bool> dp(n + 1, false);  
  7.         dp[0]=true;  
  8.         for(int i=1;i<=n;i++)  
  9.         {  
  10.             if(dp[i-1])  
  11.             {  
  12.                 int idx=i-1;  
  13.                 for(int j=idx;j<n;j++)  
  14.                 {  
  15.                     string cur=s.substr(idx,j-idx+1);  
  16.                     if(dict.count(cur)>0)  
  17.                         dp[j+1]=true;  
  18.                 }  
  19.             }  
  20.         }  
  21.         return dp[n];  
  22.     }  
  23. };  
13.Word Break II

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.

Return all such possible sentences.

For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].

A solution is ["cats and dog", "cat sand dog"].


跟第一題一樣,就是要返回所有可能的切分,, 做切分反應(yīng)是用回溯,,但是不剪枝肯定要超時(shí)。

這里用了一個(gè)math[i][j] 來(lái)表示 i--j 這一段是否可以切分,,然后在dfs的時(shí)候利用看最后剩余的子串能否切分來(lái)剪枝


  1. class Solution {  
  2. public:  
  3.     vector<string> wordBreak(string s, unordered_set<string> &dict)   
  4.     {  
  5.         int n=s.length();  
  6.         vector<vector<bool> > match(n+1,vector<bool>(n+1,false));  
  7.         for(int i=0;i<=n;i++)  
  8.             match[0][i]=true;  
  9.         for(int len=1;len<=n;len++)  
  10.         {  
  11.             for(int start=0;start+len<=n;start++)  
  12.             {  
  13.                 string tmp=s.substr(start,len);  
  14.                 if(dict.count(tmp)>0)  
  15.                     match[len][start]=true;  
  16.                 else  
  17.                 {  
  18.                     for(int left=1;left<len;left++)  
  19.                     {  
  20.                         match[len][start]=match[left][start]&&match[len-left][start+left];  
  21.                         if(match[len][start])  
  22.                             break;  
  23.                     }  
  24.                 }  
  25.             }  
  26.         }  
  27.         if(match[n][0]==false)  
  28.             return vector<string>();  
  29.         vector<string> ans;  
  30.         vector<string> had;  
  31.         dfs(s,0,match,had,ans,dict);  
  32.         return ans;  
  33.     }  
  34.     void dfs(string& s,int k,vector<vector<bool> >& match,vector<string>& had,vector<string>& ans,unordered_set<string> &dict)  
  35.     {  
  36.         int n=s.length();  
  37.         if(k>=n)  
  38.         {  
  39.             if(!had.empty())  
  40.             {  
  41.                 string ret;  
  42.                 for(int i=0;i<had.size();i++)  
  43.                 {  
  44.                     ret.append(had[i]);  
  45.                     if(i!=had.size()-1)  
  46.                         ret.push_back(' ');  
  47.                 }  
  48.                 ans.push_back(ret);  
  49.                 return;  
  50.             }  
  51.         }  
  52.         for(int len=1;k+len<=n;len++)  
  53.         {  
  54.             string tmp=s.substr(k,len);  
  55.             if(dict.count(tmp)>0 && match[n-k-len][k+len])  
  56.             {  
  57.                 had.push_back(tmp);  
  58.                 dfs(s,k+len,match,had,ans,dict);  
  59.                 had.pop_back();  
  60.             }  
  61.         }  
  62.     }     
  63. };  

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多