DP問題各種模型的狀態轉移方程


1(最長公共子串(注意和最長公共子序列區別))

兩個字符串str1str2,長度分別為(l1,l2)

dp[i][j]表示以兩個字符串分別以第i和第j個字符結尾所能達到的公共子序列的長度,由於下面涉及到i-1j-1,那么這個時候我們一般從i=1j=1開始到i<=len1, j<=len2

        if(str[i-1]=str[j-1])

            dp[i][j]=dp[i-1][j-1]+1;

        if(str[i-1]!=str[j-1])

            dp[i][j]=0;

 

                                 0 ;                                       i = 0j= 0;

就有          dp  =        dp[i][j] = dp[i-1][j-1] + 1; i > 0j> 0 ch1[i-1]= ch2[j-1];

                                 dp[i][j]=  0;                        i > 0j> 0 ch1[i-1]!= ch2[j-1];

 

注意dp[i][0]=0(0<=i<=max(l1,l2);dp[0][i]=0(0<=i<=max(l1,l2);

 

最長公共字串要求在原來字符中滿足是連續的,最長公共子序列則不要求

 

 

最長公共子序列:

 

根據最長公共子序列問題的性質,我們可以規定dp[i][j]為字符串1的前i個字符和字符串2的前j個字符的最長公共子序列的長度,  由於下面涉及到i-1j-1,那么這個時候我們一般從i=1j=1開始到i<=len1, j<=len2

             1ch1[i-1] = ch2[j-1] ,那么dp[i][j]= dp[i-1][j-1] + 1;

             2 ch1[i-1] != ch2[j-1] ,那么我們知道ch1[i]ch2[j]不可能在同一個公共子序列里出現,那么這個時候的最長的子序列可能以ch1[i]ch2[j]結尾,那么由於dp[i][j]= max {dp[i-1][j] , dp[i][j-1]};

 

          這個時候所有i=0j=0dp[i][j]= 0

                                 0 ; i = 0j= 0;

就有          dp  =       dp[i][j] = dp[i-1][j-1] + 1; i > 0j> 0 ch1[i-1]= ch2[j-1];

                                dp[i][j]= max {dp[i-1][j] , dp[i][j-1]};i > 0j> 0ch1[i-1]!= ch2[j-1];

 

 

 

2(最長上升或下降子序列)

給定一個序列a1,a2..........an;

dp[i]表示以ai結尾的最長上升子序列長度(下降相反)

核心代碼:

fori=1i<=n;i++){

       dp[i]=1;

       fork=1;k<i;k++){

            if(ak<ai&&dp[i]<dp[k]+1)

              dp[i]=dp[k]+1;

       }

}

注意最長不上升或不下降子序列問題

 

 

3(最大子序列的和問題)

給定一個序列a1,a2..........an;

求子序列的和最大問題dp[i]表示以ai結尾的子序列和,max為最大子序列和

核心:

1如果輸入的數據全部為負數則最大值就是序列中的一個最大值

2如果有正數

fori=1;i<=n;i++){

       dp[i]=dp[i-1]+ai;

       if(dp[i]<0)

          dp[i]=0;

       if(max<dp[i])

          max=dp[i];
}

 

 

4(數塔問題)

給定一個數組s[n][m]構成一個數塔求從最上面走到最低端經過的路徑和最大

我么采用至底向上的思路求解問題(注意從倒數第二行開始)

dp[i][j]表示走到第i行第j個的最大值

那么就有dp[i][j]=max{dp[i-1][j-1],dp[i-1][j]}+s[i][j];

for(i=n-1;i>=1;i--){

     for(j=1;j<=i;j++){

         dp[i][j]=max{dp[i-1][j-1],dp[i-1][j]}+s[i][j]

     }

}

最后dp[1][1]即為最大值

 

 

501背包問題)

N件物品和一個容量為V的背包。第i件物品的體積是v[i],價值是c[i]。求解將哪些物品裝入背包可使價值總和最大。

我們知道對於沒一件物品我們有兩種可能就是放與不放

dp[i][j]表示第i件物品放入容量為j的背包所得的最大價值

dp[i][j]=max{dp[i-1][j-v[i]]+c[i],dp[i-1][j]};

這里我們從j=V倒推回來的話可以優化成

dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

核心代碼:

fori=1;i<=n;i++{

       for(j=V;j>=0;j--){

            if(j>=v[i])

              dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

       }

}

dp[v]即為最大的價值

 

 

 

6(完全背包問題)

N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的體積是v[i],價值是c[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

 

這時候對於沒見物品就不是放與不放的問題了,而是放01.......

這時候我們可以像01背包一樣

dp[i][j]表示容量為j的背包第i件物品是否要再一次放入所以我們要從0-V順序循環

dp[i][j]=max{dp[i-1][j-v[i]]+c[i],dp[i-1][j]}(注意這里和01背包一樣但是求解的過程不同)

優化后:dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

核心代碼:

               fori=1;i<=n;i++{

                      for(j=v[i];j<=v;j++){//注意這里是從v[i]開始到V

                           if(j>=v[i])

                              dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

                      }

              }

注意這列求出的dp[v]是最大的因為一直疊加

 

7(多重背包問題)

N種物品和一個容量為V的背包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

 

因為對於第i種物品有n[i]+1種策略:取0件,取1件……取n[i]件。

重點:令dp[i][j]表示前i種物品恰放入一個容量為j的背包的最大價值

狀態轉移方程:dp[i][j]=max{dp[i-1][v-k*v[i]]+k*c[i]|0<=k<=n[i]};(k表示第i種物品放入k件);

核心代碼:

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

                    for(j=v;j>=0;j--){

                         for(k=1;k<=n[i];k++){

                             if(j>=k*v[i])

                                dp[i][j]=max(dp[i-1][v-k*v[i]]+k*c[i])
                         }

                    }

         

8: (二維費用的背包問題)

二維費用的背包問題是指:對於每件物品,具有兩種不同的費用;選擇這件物品必須同時付出這兩種代價;對於每種代價都有一個可付出的最大值(背包容量)。問怎樣選擇物品可以得到最大的價值。設這兩種代價分別為代價1和代價2,第i件物品所需的兩種代價分別為a[i]b[i]。兩種代價可付出的最大值(兩種背包容量)分別為VU。物品的價值為w[i]

費用加了一維,只需狀態也加一維即可。設f[i][v][u]表示前i件物品付出兩種代價分別為vu時可獲得的最大價值。

狀態轉移方程就是:

f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

 



9(最大子段和問題(最大子序列的和不同))

給定一個序列為a1,a2,a3......an;

要求:求出這個序列里面找到一個子段和最大

dp[i]表示以第i個元素結束,求出所有的“以第i個元素結束的連續數組最大和dp[i]

就有:

           1如果dp[i-1]>0,無論ai為何值,有dp[i]=dp[i-1]+ai;

           2如果dp[i-1]<=0;舍棄,重新令dp[i]=ai;(因為dp[i-1]為負數無論ai為什么值加上去都會減少)

           狀態轉移方程:dp[i]=dp[i-1]+ai (dp[i-1]>0)

                                   dp[i]=ai(dp[i-1]<=0)

 

 

12(最大m子段和)

在限制條件增加一維時,可以將狀態也相應的增加一維,來進行狀態轉移

dp[i][j]表示以第i個元素為結尾,使用j個子段所能達到的最大值(這一維的狀態,正是對應了新的限制條件!)這樣就很容易寫出

狀態轉移方程:

                    dp[i][j]= max{ dp[i - 1][j] + a[i], dp[i - k][j - 1] + a[i]}( j - 1 <= k <n - m + j).

                   1  dp[i - 1][j] + a[i] (把第i個元素包含在最后一個子段內)

                   2  dp[i - k][j - 1] + a[i], j - 1 <= k < n - m + j(i個元素單獨為一子串)

 

 

13矩陣連乘

問題描述:                 給定一序列的矩陣要求找到一種矩陣連乘的順序,使得連乘的次數最少

思路:                      建立遞推表達式,利用動態規划的方式(m[i][j]表示第i個矩陣至第j個矩陣這段的最優解,還有對於兩個矩陣M(i,j)*S(j,k)則需要i*j*k次乘法)

                                    1顯然如果i=j,則m[i][j]這段中就一個矩陣,需要計算的次數為0

                                2如果i < j,則m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]},其中k,ij之間游盪,所以i<=k<j;

                          3因為你要保證在計算m[i][j]查找m[i][k]m[k+1][j]的時候,m[i][k]m[k+1][j]已經計算出來了

所以有動態轉移方程:

                                 m[i][j]={ 0 , i=j};

                                 m[i][j]={min{m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]} , i!=j};

m[1][n]即為最終求解

 

白書上面寫道:記憶華搜索固然沒有問題,但如果要寫成遞推,無論按照i還是j的遞增或遞減均不爭確。正確的方法是按照j-i遞增的順序遞推,因為長區間的值依賴於短區間的值

模板:

int dp[MAXN][MAXN];//存儲最小的就算次數
int s[MAXN][MAXN];//存儲斷點,用在輸出上面

核心代碼:

                   int i , j ,tmp;

                   for(l= 2 ; l <= n ; l++){//j-i的長度,由於長度為1是相同的矩陣那么為0不用計算
                         for(i = 1 ; i <= n-l+1 ; i++){//由於j-i =l - 1 , 那么j的最大值為n,所以i上限為 n - l+1;
                              j = i+l-1;//由於j-i = l - 1 , 那么j = l+i-1
                             dp[i][j] = dp[i+1][j] + r[i]*c[i]*c[j];//初始化,就是k = i;
                             s[i][j] = i;
                             for(k = i+1 ; k < j ; k++){//循環枚舉k i < k < j
                                 tmp = dp[i][k] + dp[k+1][j] + r[i]*c[k]*c[j];
                                 if(dp[i][j] > tmp){
                                    dp[i][j] = tmp;//更新為最小值
                                    s[i][j] = k;
                            }
                        }
                    }
               }

 

輸出:

        //遞歸調用輸出
       void output(int i , int j){
              if(i == j){
                  printf("A%d" , i);//當兩個相等的時候就不用繼續遞歸就輸出A
                  return;//返回上一層
              }

             else{
                  printf("(");
                  output(i , s[i][j]);
                  printf(" x ");
                  output(s[i][j]+1 , j);
                  printf(")");
             }     
      }

 

 

14區間DP

區間動態規划問題一般都是考慮,對於每段區間,他們的最優值都是由幾段更小區間的最優值得到,是分治思想的一種應用,將一個區間問題不斷划分為更小的區間直至一個元素組成的區間,枚舉他們的組合,求合並后的最優值。
         1  設F[i,j]1<=i<=j<=n)表示區間[i,j]內的數字相加的最小代價
         2  最小區間F[i,i]=0(一個數字無法合並,∴代價為0

            3  每次用變量ki<=k<=j-1)將區間分為[i,k][k+1,j]兩段


         4《區間DP模板,代碼》
              for(int p = 1 ; p <= n ; p++){//p是區間的長度,作為階段
           for(int i = 1 ; i <= n-p ; i++){//i是窮舉區間的起點
               int j = i+p;//j為區間的終點
               for(int k = i+1 ; k < j ; k++)//狀態轉移
                   dp[i][j] = min{dp[i][k]+dp[k+1][j]+w[i][j]};//這個是看具體的狀態轉移方程

                  或 dp[i][j] = max{dp[i][k]+dp[k+1][j]+w[i][j]};//求最大

                  或  dp[i][j] = min{dp[i][k]+dp[k][j]+w[i][j]}//有的是要從k開始不是k+1

              }
       }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM