動態規劃 &

1、 動態規劃(Dp問題):解決問題的關鍵點算法

        1) 遞推公式:(最有子結構)數組

        2) 數據的初始化 優化

用例分析:spa

    a.  01 揹包的問題(Knapsack Problem).net

    定義:  一個揹包的容量V; 存在N個物品:w[i]是揹包的的容量,v[i]是揹包的價值;求:揹包能存放的最大價值?code

    hint: 每一個揹包存在兩個狀態:0不入包,1入包orm

    分析: 定義 dp[i][j]:前i個物品 放入 容量爲j的揹包 時 的最大價值;blog

             dp[i] 與 dp[i-1]的關係  =>對於物品i存在兩個狀態:0->不放入揹包;1->放入揹包;排序

                * 0時: dp[i][j] = dp[i-1][j];ci

                * 1時: dp[i][j] = dp[i-1][j-w[i]] + v[i];

             因此遞推公式:dp[i][j] = max(  dp[i-1][j],   dp[i-1][ j-w[i] ] + v[i]   );

    初始化:i = 0時: dp[0][0-V] = 0;

               j = 0時: dp[0-N][0] = 0;     取值範圍: 0<= i <= N; 0<= j <= V;

    返回: return dp[N][V];

int  knap01(vector<int> w, vector<int> v, int V)
{
     if(w.size() < 0) return -1;
     
     int N = w.size(); //揹包的數量
     //0<=i<=N; 0<=j<=V;
     vector< vector<int> >dp(N+1,vector<int>(V+1));

      //init   初始化
      for(int i=0; i<=N; i++)
               dp[i][0] = 0;
      for(int j=0; j<=V; j++)
               dp[0][j] = 0;
      
      //dp過程     
      for(int i=1; i<=N; i++)
      {
           for(int j=1; j<=V; j++)
           {
               if(j < w[i-1]){
                    dp[i][j] = dp[i-1][j];
                    cout << dp[i][j] << ' ';
                    continue;
                }
                int v0 = dp[i-1][j];                  //i是物品不入包的狀況
                int v1 = dp[i-1][j-w[i-1]] + v[i-1];  //i物品入包的狀況
                dp[i][j] = v0 > v1 ? v0 : v1;         //取max
                cout << dp[i][j] << ' ';
           }
           cout << endl;
       }
       return dp[N][V];
}


aa. 揹包問題的升級: 即徹底揹包問題

     與a的不一樣: 物品i能夠取無限個,而不是隻能取一個;

     定義:一個揹包總容量爲V;  如今有N個物品,第i個 物品體積爲weight[i],價值爲value[i],每一個物                品都有無限多件;  如今往揹包裏面裝東西,怎麼裝能使揹包的內物品價值最大?

和01揹包問題惟一不一樣的是:01揹包問題是在前一個子問題(i-1種物品)的基礎上來解決當前問題(i種物                              品),向i-1種物品時的揹包添加第i種物品;

                   而徹底揹包問題是在解決當前問題(i種物品),向i種物品時的揹包添加第i種物品。 

    dp[i] 與 dp[i-1]的關係  =>對於物品i存在兩個狀態:0->不放入揹包;1->放入揹包;

     * 0時: dp[i][j] = dp[i-1][j];

     * 1時: dp[i][j] = dp[i][j-w[i]] + v[i];   //代碼主要的區別

     因此遞推公式:dp[i][j] = max(  dp[i-1][j],   dp[i][ j-w[i] ] + v[i]  );

int  knap01(vector<int> w, vector<int> v, int V)
{
     if(w.size() < 0) return -1;
     
     int N = w.size(); //揹包的數量
     //0<=i<=N; 0<=j<=V;
     vector< vector<int> >dp(N+1,vector<int>(V+1));
      //init   初始化
      for(int i=0; i<=N; i++)
               dp[i][0] = 0;
      for(int j=0; j<=V; j++)
               dp[0][j] = 0;
      
      //dp過程     
      for(int i=1; i<=N; i++)
      {
           for(int j=1; j<=V; j++)
           {
               if(j < w[i-1]){
                    dp[i][j] = dp[i-1][j];
                    cout << dp[i][j] << ' ';
                    continue;
                }
                int v0 = dp[i-1][j];                  //i是物品不入包的狀況
                int v1 = dp[i][j-w[i-1]] + v[i-1];    //i物品入包的狀況; 徹底揹包問題
                dp[i][j] = v0 > v1 ? v0 : v1;         //取max
                cout << dp[i][j] << ' ';
           }
           cout << endl;
       }
       return dp[N][V];

    a.  Fibonacci序列的問題     DP  實現  http://my.oschina.net/u/573270/blog/479644

01揹包問題的優化:

    a:  01揹包問題優化

計算f[i][j]能夠看出,在計算f[i][j]時只使用了f[i-1][0……j],沒有使用其餘子問題,所以在存儲子問題的解時,只存儲f[i-1]子問題的解便可。再進一步思考,計算f[i][j]時只使用了f[i-1][0……j],沒有使用f[i-1][j+1]這樣的話,咱們先計算j的循環時,讓j=M……1,只使用一個一維數組便可。

int knap02(vector<int>w, vector<int> v, int V)
{
      if(w.size() < 0) return -1;
      
      int N = w.size();
      vector<int> dp(V+1,0);
      
      //dp
      for(int i=1; i<=N;i++)
      {
           for(int j=V; j>=1; j--)   //經過逆序,f[i-1][..]
    	  {
              if(j < w[i-1]){
            	  dp[j] = dp[j-1];
            	  continue;
              }
              int v0 = dp[j-1];
              int v1 = dp[j - w[i-1]] + v[i-1];
              dp[j] = v0 > v1 ? v0 : v1;
              cout << dp[j] << ' ';
    	  }
      }
      return dp[V];
}

    aa: 徹底揹包問題的優化

int knap02(vector<int>w, vector<int> v, int V)
{
      if(w.size() < 0) return -1;
      int N = w.size();
      vector<int> dp(V+1,0);
      
      //dp
      for(int i=1; i<=N;i++)
      {
          for(int j=1; j<=V; j++)
          {
              if(j < w[i-1]){
                 dp[j] = dp[j-1];
                 continue;
              }
              int v0 = dp[j];        //徹底揹包
              int v1 = dp[j - w[i-1] ] + v[i-1];
              dp[j] = v0 > v1 ? v0:v1;
          }
      }
      return dp[V];
}

貪心算法求解揹包問題:

01揹包不能用貪心求解: 徹底揹包問題  能夠用貪心求解:

      思路:

            計算每一個揹包的單位重量價值v/w;從大到小排序; 選取價值大的物品加入揹包;若是超過揹包稱重V,則選區次重要的物品加入揹包;

struct good{
   int w;
   int v;
   double p;
};
bool cmp(good &g1, good& g2)
{
     return g1.p < g2.p;
}
int greedy(vector<int>w, vector<int>v, int V)
{
   vector<good> goods;
   for(int i=0; i<w.size(); i++)
   {
        good g;
        g.w = w[i];
        g.v = v[i];
        g.p = g.v / g.w;
        goods.push_back(g);
   }
   
   //sort
       sort(goods.begin(), goods.end(),cmp);
   //greedy
       int weight = 0;
       int value = 0;
   for(int i=0; i<goods.size(); i++)
   {
       while(weight + goods[i].w <= V){
           weight += goods[i].w;
           value  += goods[i].v;
      }
   }
   return value;  //返回的值不必定是最優解
}

  =>揹包問題能夠應用到不少的實際環境中;

  1. 最長公共字序列  VS  最長公共子串

    最長公共字序列:

    dp:

         dp[i][j] 表示 str1的前i個字符   與  str2的前j個字符的公共字序列

          遞推關係:

          str1[i] = str2[j]     則, dp[i][j] =  dp[i-1][j-1] + 1;  

          str1[i] != str2[j]      則,dp[i][j] 的值時dp[i-1][j]  與 dp[i][j-1]的最大值; dp[i][j] = max( dp[i-1][j],  dp[i][j-1] );          

//輸出長度 =>輸出公共子序列
int lcs(string str1, string str2)
{
    if(str1.size() < 0 || str2.size() < 0) return 0;
    
    int row = str1.size();
    int col = str2.size();
    //init
    vector< vector<int > > dp(row+1, vector<int>(col+1));
    for(int i=0; i<= row; i++)
          dp[i][0] = 0;
    for(int j=0; j<=col; j++)
          dp[0][j] = 0;
          
    //dp
    for(int i=1; i<=row; i++)//行
    {
          for(int j=1; j<=col; j++)
          {
               if(str1[i-1] == str2[j-1])
                    dp[i][j] = dp[i-1][j-1] + 1;
               else{//str1[i-1] != str2[j-1]
                    dp[i][j] = dp[i-1][j] > dp[i][j-1] ? dp[i-1][j] : dp[i][j-1];
               }
         }
    }
   
    //輸出最長公共字序列:只是輸出了其中的一個
    string substr="";
    int i = row;
    int j = col;
    for(;i> 0 && j > 0;)
    {
          if(str1[i-1] == str2[j-1]){
                substr+=str1[i-1];
                i--;
                j--;
          }
          else{
               if(dp[i-1][j] > dp[i][j-1]){
                   i--;  //
               }else{
                   j--;
               }
         }
    }
    cout << substr << endl;
    
    return dp[row][col];
}

  最長公共子串:

    遞推公式

    str1[i] = str2[j]    則 dp[i][j] = dp[i-1][j-1] + 1;

    str1[i] <> str2[j] 則 dp[i][j] = 0;

int lcs2(string str1, string str2)
{
    if(str1.size() < 0 || str2.size() < 0) return 0;
    int row = str1.size();
    int col = str2.size();
    
    vector< vector<int> > dp(row+1, vector<int>(col+1));
    for(int i=0; i<=row; i++)
       dp[i][0] = 0;
    for(int j=0; j<=col; j++)
       dp[0][j] = 0;
    
    //dp
    int max = 0;
    for(int i=1; i<=row; i++)
    {
        for(int j=1; j<=col; j++)
        {
             if(str1[i-1] == str2[j-1])
                 dp[i][j] = dp[i-1][j-1] + 1;
             else{
                 dp[i][j] = 0;     //區別的地方
             }
             max = max > dp[i][j] ? max : dp[i][j];
         }
    }
    
    //根據對角線就能求出子串:
    return max;
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息