1. 程式人生 > >揹包問題(動態規劃)

揹包問題(動態規劃)

01揹包問題

有n個重量和價值分別為wi,vi的物品,從這些物品中挑選出總重量不插過W的物品,求所有挑選方案中價值總和的最大值

限制條件

1<=n<=100

1<=wi,vi<=100

1<=W<=10000

常規通法DFS

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品數量
int w[100+5];//物品質量
int v[100+5];//物品價值
int W;//揹包總總質量

//i代表物品編號,j代表揹包剩餘質量
int dfs(int i,int j)
{
    int res;
    if(i==n)
    {
        //已經沒有剩餘的物品了
        res=0;
    }
    else if(j<w[i])
    {
        //揹包可利用價值小於該物品價值
        res=dfs(i+1,j);
    }
    else
    {
        //      不挑選     挑選
        res=max(dfs(i+1,j),dfs(i+1,j-w[i])+v[i]);
    }
    return res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);
        printf("%d\n",dfs(0,W));
    }
    return 0;
}

分析:在此做法中,我們因為沒有進行剪枝優化,所以進行了多次對同一狀態進行搜尋,因此我們可以通過利用一個數組dp[i][j]進行儲存狀態,即所謂的記憶化搜尋

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品數量
int w[100+5];//物品質量
int v[100+5];//物品價值
int W;//揹包總總質量

int dp[100+5][10000+5];//i代表物品編號,j代表揹包剩餘質量,依據題目限制條件寫出陣列範圍

//i代表物品編號,j代表揹包剩餘質量
int dfs(int i,int j)
{
    int res;
    if(dp[i][j]>=0)
    {
        return dp[i][j];
    }
    if(i==n)
    {
        //已經沒有剩餘的物品了
        res=0;
    }
    else if(j<w[i])
    {
        //揹包可利用價值小於該物品價值
        res=dfs(i+1,j);
    }
    else
    {
        //         不挑選          挑選
        res=max(dfs(i+1,j),dfs(i+1,j-w[i])+v[i]);
    }
    return dp[i][j]=res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        //初始化dp陣列
        memset(dp,-1,sizeof(dp));
        
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);
        
        printf("%d\n",dfs(0,W));
        
    }
    return 0;
}

現在我們研究儲存狀態的記憶化陣列,設dp[i][j]儲存的是此刻此時揹包中左右物品的價值總和,i代表的是我當前已經記錄的狀態,i+1代表的是揹包此時所要面對的物品的編號(假設我是揹包,我已經經歷過了i號,現在面對的是i+1號,我到底是取還是不取i+1號呢?)

所以我們可以定義

dp[i+1][j]:從0到i這i+1個物品選出總重量不超過j的物品時的總價值

dp[0][j]=0 

所以dp[i+1][j]存在兩種狀態

第一:dp[i+1][j]=dp[i][j]  (j<w[i])代表當前揹包剩餘重量已經無法滿足儲存當前物品重量

第二:dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])(結合記憶化搜尋,可以理解為在選擇與不選擇這個物品進行一下對比

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品數量
int w[100+5];//物品質量
int v[100+5];//物品價值
int W;//揹包總總質量

int dp[100+5][10000+5];//i代表物品編號,j代表揹包剩餘質量,依據題目限制條件寫出陣列範圍

void solve()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=W;j++)
        {
            if(j<w[i])
            {
                dp[i+1][j]=dp[i][j];
            }
            else
            {
                dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
            }
        }
    }
    printf("%d\n",dp[n][W]);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        //初始化dp陣列
        memset(dp,0,sizeof(dp));

        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);

        solve();

    }
    return 0;
}