DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP二维DP)
Posted 嚜寒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP二维DP)相关的知识,希望对你有一定的参考价值。
1)
背包基础,先以01背包、求背包所装物品价值之和的最大值、不要求恰好装满时,易于理解的二维DP数组存储为例:
#include <iostream>
#include <string.h>
using namespace std;
int dp[1010][1010];
int vp[1010];
int wp[1010];
int main()
int kase;cin>>kase;
while(kase--)
memset(dp,0,sizeof(dp));
int n,v;cin>>n>>v;
int v1,w1;
for(int i=1;i<=n;i++)
cin>>vp[i];
for(int i=1;i<=n;i++)
cin>>wp[i];
for(int i=1;i<=n;i++)
v1=vp[i];w1=wp[i];
for(int j=0;j<=v;j++)//正序、逆序,皆可,因为有第一唯i的存在,保证了每一次更新都保证不会放入相同的物品
if(j>=w1)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w1]+v1);
else
dp[i][j]=dp[i-1][j];//假如第i个因为体积原因放不进去,那么d第i个的状态继承第i-1的状态(如果不操作,则为初始值0)
cout<<dp[n][v]<<endl;
2)以01背包,采用方便写的一维DP数组,求背包所装物品价值之和最大值,不要求恰好装满背包为例:
//三个小分支:
//改为求完全背包时(¥1处有变化)
//改为求背包所装物品价值之和的最小值时,(#1与#2处代码,有变化)
//改为要求恰好装满背包时(@1处代码有变化)
#include <iostream>
#include <string.h>
using namespace std;
const int inf=100000000;//#1求背包所装物品价值之和的最大值时,inf为负无穷,如果求最小值,inf为正无穷,具体写几个0,看题目要求的限制
int main()
int kase;cin>>kase;
int value[1010];
int volume[1010];
int dp[1010];
while(kase--)
int n;cin>>n;
int bag;cin>>bag;
memset(dp,0,sizeof(dp));/*@1不要求恰好装满背包时,则dp[0],和dp[1...n]都置为0。@2为替换代码。理由在@2下面。
/* //@2要求恰好装满背包时,则dp[0]=0,dp[1...n]都为正无穷或者负无穷(取决于题目是求最大值还是最小值,看#1处)
dp[0]=0;
for(int i=1;i<=bag;i++)
dp[i]=inf;
*/
/*
当我们把1到n初始化为无穷值,只有0是初始化为0时,这时其他不变,一切更新照常,所不同的是,只有以0作为基础的更新的值处于正常范围,而其他值都因为初始无穷的原因,每一次更新后的数都大于等于正无穷(如果取最小值则每一次更新后还是正无穷,如果取最大值,没一次更新都大于正无穷),那么区分是否能恰好装满,只需要看最后一个状态的值是不是正常范围内的值即可。举例如下:
f为负无穷,g为正无穷,sum为背包总体积,v为物品价值,w为物品体积
sum:8
v w
3 2
4 3
w:0->n
要求恰好装满时,将0为0,将1,2,3...9都非0的初始值,又因为取最大值,所以非0的初始值是负无穷:
初始值: 0 1 2 3 4 5 6 7 8 9
0 f f f f f f f f f
0+2 f+2 f+2 2+2 f+2+2 f+2+2 2+2+2
0+3 f+3 f+3 2+3 3+3 f+3+3
(
先放大数或者先放小数并没有区别,举例如下,先放v==4,w==3
初始值: 0 1 2 3 4 5 6 7 8 9
0 f f f f f f f f f
0+3 f+3 f+3 f+3 3+3 f+3+3
0+2 f+2 f+2 2+2 f+2+3 f+2+3 2+2+2
)
不要求恰好装满时,此时0到9全部为0,不论取最大值还是取最小值,都是0做初始值:
初始值: 0 1 2 3 4 5 6 7 8 9
0 0 0 0 0 0 0 0 0 0
2 2 2 2+2 2+2 2+2 2+2+2
*/
for(int i=0;i<n;i++)
cin>>value[i];
for(int i=0;i<n;i++)
cin>>volume[i];
for(int i=0;i<n;i++)
for(int j=bag;j>=volume[i];j--)//*¥1,01背包每一种物品只能放一次,则此处要j从大到小,采用逆序(以保证,每一次更新放入的物品都不重复,都是在上一个物品放置后的情况下更新的),而完全背包,j从小到大,正序,这样使一种物品可以重复放置。两者区别具体讲,当j逆序由大到小如由j==3到j==2变化时,假设volume[i]=1,j-volume[i]也是逐渐减小,依次对应为,2、1,那么状态转移方程就是dp[3]=max(dp[3],dp[2]+value[i]);dp[2]=max(dp[2],dp[1]+value[i]);如果是完全背包,j正序由小变大,那么dp[2]=max(dp[2],dp[1]+value[i]);dp[3]=max(dp[3],dp[2]+value[i]);最后一个式子中,进行比较所用的dp[2]刚刚被更新过,是现在这个物品放置一次、更新后的情况而非上一个物品放置后的情况!所以使同一个物品可以重复放置!*/
dp[j]=(dp[j]>dp[j-volume[i]]+value[i])?dp[j]:dp[j-volume[i]]+value[i];//#2,此处取大的一方更新原来的数,如果求最小值自然取小的一方更新原来的数。例如下面
/*
dp[j]=(dp[j]<dp[j-volume[i]]+value[i])?dp[j]:dp[j-volume[i]]+value[i];//
*/
cout<<dp[bag]<<endl;
3)练习题目
01背包、不要求恰好装满、求最大值,入门题,hdu2602;
完全背包、要求恰好装满、求最小值,入门题,hdu1114;
hdu2602 AC代码:
#include <iostream>
#include <string.h>
#include <map>
using namespace std;
//map <int,int> bone;
int main()
int kase;cin>>kase;
int value[1010];
int volume[1010];
int dp[1010];
while(kase--)
int n;cin>>n;
int bag;cin>>bag;
memset(dp,0,sizeof(dp));
//int value,volume;
for(int i=0;i<n;i++)
cin>>value[i];
//bone[value]=volume;
for(int i=0;i<n;i++)
cin>>volume[i];
for(int i=0;i<n;i++)
for(int j=bag;j>=0;j--)
if(j>=volume[i])
dp[j]=(dp[j]>dp[j-volume[i]]+value[i])?dp[j]:dp[j-volume[i]]+value[i];
//else
//dp[j]=dp[j];
cout<<dp[bag]<<endl;
hdu1114 AC代码:
#include <iostream>
#include <string.h>
using namespace std;
struct coin
int value;
int weight;
coins[510];
int dp[10010];
//求最小值,用正无穷:
long long int inf=100000000;
int main()
int kase;cin>>kase;
while(kase--)
int emp,full;cin>>emp>>full;
int volume=full-emp;
int n;cin>>n;
//恰好装满,不同初始值:
dp[0]=0;
for(int i=1;i<=volume;i++)
dp[i]=inf;
for(int i=0;i<n;i++)
cin>>coins[i].value;
cin>>coins[i].weight;
//完全背包,正序:
for(int i=0;i<n;i++)
for(int j=coins[i].weight;j<=volume;j++)
dp[j]=min(dp[j],dp[j-coins[i].weight]+coins[i].value);
if(dp[volume]==inf)
cout<<"This is impossible."<<endl;
else
printf("The minimum amount of money in the piggy-bank is %d.\\n",dp[volume]);
以上是关于DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP二维DP)的主要内容,如果未能解决你的问题,请参考以下文章