1. 程式人生 > >01揹包(講解)

01揹包(講解)

本來還覺得01揹包是動態規劃中比較基礎的部分,沒想到現在看了一下覺得好難...

這題就是01Knapsack問題,我參考了一下Hawstein的blog,先來舉一些例子吧:

讓我假設現在的揹包的容量是C=10;

物品編號: 1 2 3

物品重量: 5 6 4

物品價值:20 10 12

用v[i]表示物品價值,w[i]表示物品重量,要使得放入揹包的物品價值最大化,我們知道用貪心是不行的!

------------------------------------------------------------------------------------------------------------------

所以接下來開始動規:

首先定義狀態dp[i][j]以j為容量為放入前i個物品(按i從小到大的順序)的最大價值,那麼i=1的時候,放入的是物品1,這時候肯定是最優的啦!

那考慮一下j,j是當前容量,如果j<5,那麼是不是就不能放,dp[1][j](j<5)=0;那如果j>5,就可以放了,dp[1][j](j>=5)=20;

接著i=2放兩個物品,求的就是dp[2][j]了,當j<5的時候,是不是同樣的dp[2][j](j<5)等於0;那當j<6是不是還是放不下第二個,只能放第一個;

那j>6呢?是不是就可以放第二個了呢?是可以,但是明顯不是最優的,用腦子想了一下,發現dp[2][j](j>6)=20,這個20怎麼來的呢,當然是從前一個狀態來的(注意這裡就可以分為兩種情況了):一種是選擇第二個物品放入,另一種還是選擇前面的物品;

讓我們假設一下j=10吧,可能會比較好理解!這時候:dp[2][10] = max(dp[1][10-w[2]])+v[2],dp[1][10]);

dp[2][10] = max(dp[1][4])+10,dp[1][10]);

是不是很明顯了呢,dp[1][4])+10是選擇了第二個,於是容量相應就減少成4,之前已經得出dp[1][4]=0,就是說選了物品2,物品1就選不了了;dp[1][10]是不選擇第二個,只選擇第一個dp[1][10]是等於20的,於是得出dp[2][10]=20;

到這裡就可以了,依次類推,動態轉移方程為:dp[i][j] = max(dp[i-1][j-w[i]])+v[i],dp[i-1][j]);

但是好像還有一些問題沒考慮完.........

看回例子:

物品編號: 1 2 3

物品重量: 5 6 4

物品價值:20 10 12

我們知道dp[1][j](j<5)=20,dp[2][j](j=5)的時候是多少呢?我們看到動態轉移方程並沒有考慮j<w[i]的情況,但是我們可以加進去,由於dp[2][5]我們看出來是等於5的,為什麼?因為不能選第二個,只能選第一個,所以.....dp[2][5]是不是剛好等於dp[1][5]了呢!所以當j<w[i]的時候,dp[i][j] = dp[i-1][j]就好了,是不是很神奇呢!

複製程式碼
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 int w[105], val[105];
 6 int dp[105][1005];
 7 
 8 int main()
 9 {
10     int t, m, res=-1;
11     cin >> t >> m;
12     for(int i=1; i<=m; i++)
13         cin >> w[i] >> val[i];
14     
15     for(int i=1; i<=m; i++) //物品 
16         for(int j=t; j>=0; j--) //容量 
17         {
18             if(j >= w[i])
19                 dp[i][j] = max(dp[i-1][j-w[i]]+val[i], dp[i-1][j]);
20             else
21                 dp[i][j] = dp[i-1][j];           
22         }
23     cout << dp[m][t] << endl;
24     return 0;
25 }                                 
複製程式碼