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 }