DP專題 4 | 骨頭收集愛好者 - POJ 1458( 0-1揹包)

揹包問題是DP裏面變化比較多的問題,可以參考網上的《揹包9講》,另外還是閱讀《算競入門》和《算競進階》,講的最全的肯定是揹包9講,基本上把所有變形都講了一遍,但是把問題講的最清楚應該還是算競進階,特別是本篇的0-1揹包。

 

進階裏面比較清晰的講解了如何從二維數組變成滾動一維數組,講解了爲什麼一維數組是倒序,而二維數組是順序。進而也能很清晰的講解完全揹包問題。

 

OK,還是回到DP的轉移方程,0-1揹包的方程:

 

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

 

簡單的說就是當前選擇前i個物品,耗費j個容量所能夠取得最大價值。

 

如果不選擇第i個物品,那麼dp[i][j] = dp[i-1][j],

如果選擇第i個物品,那麼dp[i][j] = dp[i-1][j-C[i]] + V,因爲選擇了i,剩餘容量只剩下j-C[i],價值+V。

 

這個方程算是揹包問題裏面最容易理解的。

 

接着需要理解如果從二維數組變成一維數組。

 

因爲在迭代過程中,把i的階段分爲奇數和偶數2部分,每一次交替過程中,奇數部分會用到偶數部分的值,偶數部分會用到奇數部分的值,不會衝突,也就是說只用i={0,1}來進行循環即可。這樣就去掉了i的這一個維度。

 

具體可以參考進階書籍~

 

然後爲什麼一維數組是逆序的呢?

 

一維數組逆序是爲了讓j在降序的過程中使用i-1的迭代值,如果是升序,則會造成dp[j]使用了i的迭代值。一維數組是在二維數組邏輯下的代碼優化,本質還是二維的!要考慮隱藏的i。

 

來看題目:

 

Problem Description

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …

許多年前,有個骨頭收集愛好者BC,他收集了各種骨頭,比如狗骨頭、牛骨頭。。。(自己腦補)


The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

 

BC有一個容量爲V的袋子,骨頭都是放在裏面。骨頭有不同的價值和大小,現在告訴你每個骨頭的價值,你能算出這個袋子能夠裝的最大價值?

 

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

第一行是測試用例數量。

每個測試用例有3行,第一行是骨頭數量N、揹包大小V。

第二行是N個骨頭的價值。

第三行是N個骨頭的大小。

 

Output

One integer per line representing the maximum of the total value (this number will be less than 231).

 

Sample Input

1

5 10

1 2 3 4 5

5 4 3 2 1

 

Sample Output

14

 

0-1揹包入門基礎題。

 

源代碼:C++

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <set>
#include <string>
#include <vector>
using namespace std;

int dp[1000][1000];

int solve(int N, int V, vector<int> &values, vector<int> &volumes) {
    for(int i=1; i<=N; ++i){
        for(int j=volumes[i]; j<=V; ++j){
            dp[i][j] = std::max(dp[i-1][j], dp[i-1][j-volumes[i]] + values[i]);
        }
    }
    return dp[N][V];
}

int main() {
#ifdef __MSYS__
    freopen("test.txt", "r", stdin);
#endif
    // dp[i][j] = max{dp[i-1][j], dp[i-1][j-C[i]] + V}
    int n;
    int N, V;
    while (cin >> n) {
        while (cin >> N >> V) {
            vector<int> values(N, 0);
            vector<int> volumes(V, 0);

            for (int i = 0; i < N; ++i) {
                cin >> values[i];
            }
            for (int i = 0; i < N; ++i) {
                cin >> volumes[i];
            }
            cout << solve(N, V, values, volumes) << endl;
        }
    }

    return 0;
}

一維代碼:C++

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <set>
#include <string>
#include <vector>
using namespace std;

int dp[1000];

int solve(int N, int V, vector<int> &values, vector<int> &volumes) {
    for(int i=1; i<=N; ++i){
        for(int j=V; j>=volumes[i]; --j){
            dp[j] = std::max(dp[j], dp[j-volumes[i]] + values[i]);
        }
    }
    return dp[V];
}

int main() {
#ifdef __MSYS__
    freopen("test.txt", "r", stdin);
#endif
    // dp[i][j] = max{dp[i-1][j], dp[i-1][j-C[i]] + V}
    int n;
    int N, V;
    while (cin >> n) {
        while (cin >> N >> V) {
            vector<int> values(N, 0);
            vector<int> volumes(V, 0);

            for (int i = 0; i < N; ++i) {
                cin >> values[i];
            }
            for (int i = 0; i < N; ++i) {
                cin >> volumes[i];
            }
            cout << solve(N, V, values, volumes) << endl;
        }
    }

    return 0;
}

個人公衆號(acm-clan):ACM算法日常

專注於基礎算法的研究工作,深入解析ACM算法題,五分鐘閱讀,輕鬆理解每一行源代碼。內容涉及算法、C/C++、機器學習等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章