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;
}