01揹包問題 -- 經典動態規劃題

01揹包問題

問題描述:有n個重量和價值分別爲Wi,Vi的物品,從這些物品中挑選出總重量不超過W的物品,求怎麼挑選才能使價值最大;


首先可以使用搜索來看看,吧每件物品放入揹包試試,找出最優解。

// 01揹包問題
// 問題描述:
// 有一個揹包可以存放W重量的物品,有n樣物品,價值分別爲v1,v2,v3,……vn,
// 重量分別爲w1,w2,w3,……wn,求怎麼放才能讓價值最大

// 首先不使用動態搜索,只使用深度搜索來解決問題。
// 搜索的方法是

#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_N = 1005;
int W;      // 揹包最大重量
int N;      // 物品數量
struct Res  // 定義物品
{
    int weight;
    int value;
}res[MAX_N];
int dfs(int i, int j);      // i代表搜索第幾個物品, j代表還可以放入多少重量的物品

int main()
{
    cout <<"請輸入揹包的最大存放重量";
    cin >> W;
    cout << "請輸入物品的數量:";
    cin >> N;
    cout << "請輸入每件物品的重量和價值:\n";
    for(int i = 0; i < N; i++)
        cin >> res[i].weight >> res[i].value;

    int ans = dfs(0, W);
    cout << ans << endl;

    return 0;
}

int dfs(int i, int j)
{
    int ans;

    if(i == N)      // 如果搜索到最後一個
        ans = 0;
    else if(j < res[i].weight)  // 如果當前的物品不能放入揹包
        ans = dfs(i + 1, j);
    else            // 比較挑選和不挑選的情況都嘗試一下返回最優解
        ans = max(dfs(i + 1, j), dfs(i + 1, j - res[i].weight) + res[i].value);

    return ans;
}

每一次搜索都是要兩次分支,所以如果物品一多就會運行效率特別慢。

運行截圖:

可以看到如果只用搜索的話會有重複的調用。所以我可以吧第一次的計算記錄下來

所以我們只要把第一次計算的結果保留到一個數組中記錄下來,如果再次遇到重複的計算直接使用這個計算結果即可

// 使用二維數組記錄結果,避免了搜索中的重複調用,優化搜索過程,稱爲記憶化搜索
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 1005;
struct RES
{
    int weight;
    int value;
}res[MAX_N];
int W, N;
int DP[MAX_N][MAX_N];

int dfs(int i, int j);

int main()
{
    cout <<"請輸入揹包的最大存放重量";
    cin >> W;
    cout << "請輸入物品的數量:";
    cin >> N;
    cout << "請輸入每件物品的重量和價值:\n";
    for(int i = 0; i < N; i++)
        cin >> res[i].weight >> res[i].value;

    memset(DP, -1, sizeof(DP));
    int ans = dfs(0, W);
    cout << ans << endl;

    return 0;
}

int dfs(int i, int j)
{
    if(DP[i][j] >= 0)
        return DP[i][j];

    int reslut;
    if(i == j)
        reslut = 0;
    else if(j < res[i].weight)
        reslut = dfs(i + 1, j);
    else
        reslut = max(dfs(i + 1, j), dfs(i + 1, j - res[i].weight) + res[i].value);

    return DP[i][j] = reslut;
}

這樣略微改進就可以優化整個程序,是可接問題的規模大幅度提高


然後要上一個真正的動態規劃啦,其實根據上面的記憶化搜索就可以推出狀態轉移方程了:

DP = DP[i + 1][j];    (j < res[i].weight)
DP = max(DP[i + 1][ j], DP[i + 1][ j - res[i].weight] + res[i].value)

根據整個狀態轉移方程就可以寫出程序了

代碼如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 1005;
struct RES
{
    int weight;
    int value;
}res[MAX_N];
int W, N;
int DP[MAX_N][MAX_N];

int main()
{
     cout <<"請輸入揹包的最大存放重量";
    cin >> W;
    cout << "請輸入物品的數量:";
    cin >> N;
    cout << "請輸入每件物品的重量和價值:\n";
    for(int i = 0; i < N; i++)
        cin >> res[i].weight >> res[i].value;

    memset(DP, 0, sizeof(DP));
    for(int i = N - 1; i >= 0; i--)
        for(int j = 0; j <= W; j++)
            if(j < res[i].weight)
                DP[i][j] = DP[i + 1][j];
            else
                DP[i][j] = max(DP[i + 1][j], DP[i + 1][j -res[i].weight] + res[i].value);
    cout << DP[0][W];

    return 0;
}


發佈了42 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章