揹包問題(動態規劃)
阿新 • • 發佈:2019-01-07
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;
}