零一背包倒着搜,完全背包正着搜,多重背包二进制存转化成01背包。
0-1背包
对于每种物品只有取与不取两个选择。
洛谷p1048
//https://www.luogu.com.cn/problem/P1048
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;
int cost[maxn];
int val[maxn];
int dp[105][maxn]; //dp[i][j] 当背包容积为j时,放入i的物品所获得的最大价值
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&cost[i],&val[i]);
}
for(int i=0;i<m;i++)
dp[0][i]=0;
for(int i=1;i<=m;i++)
for(int j=t;j>=0;j--)
{
if(j>=cost[i]) //背包容积可以放下当前物品
dp[i][j]=max(dp[i-1][j],dp[i-1][j-cost[i]]+val[i]);
else //放不下
dp[i][j]=dp[i-1][j];
}
printf("%d
",dp[m][t]);
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;
int cost[maxn];
int val[maxn];
int dp[1005]; //dp[j] 当背包容积为j时,所获得的最大价值
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&cost[i],&val[i]);
}
for(int i=0;i<m;i++)
dp[i]=0;
for(int i=1;i<=m;i++) //滚动数组,依次遍历每个物品
for(int j=t;j>=cost[i];j--) //当j小于cost[i]时,直接沿用原来的值 与二维 else dp[i][j]=dp[i-1][j];对应
{
dp[j]=max(dp[j],dp[j-cost[i]]+val[i]);
}
printf("%d
",dp[t]);
return 0;
}
完全背包
每种物品可以取无限多个。(在大部分情况下,用贪心思想,选性价比最高的物品其实就是答案,大概80%)。
洛谷p2871
//https://www.luogu.com.cn/problem/P2871
//因数据范围问题,此代码无法ac,贴在这只是帮助理解
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 205;
int cost[maxn];
int val[maxn];
int dp[105][maxn]; //dp[i][j] 当背包容积为j时,可放入i种物品,所获得的最大价值
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&cost[i],&val[i]);
}
for(int i=0;i<m;i++)
dp[0][i]=0;
for(int i=1;i<=m;i++)
for(int j=0;j<=t;j++)
{
if(j>=cost[i]) //背包可以再放一个i类型的物品
dp[i][j]=max(dp[i-1][j],dp[i][j-cost[i]]+val[i]);
else
dp[i][j]=dp[i-1][j];
}
printf("%d
",dp[m][t]);
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn = 10000;
int cost[maxn];
int val[maxn];
int dp[100000]; //dp[i][j] 当背包容积为j时,放入i的物品所获得的最大价值
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&cost[i],&val[i]);
}
for(int i=0;i<m;i++)
dp[0]=0;
for(int i=1;i<=m;i++)
for(int j=cost[i];j<=t;j++)
{
dp[j]=max(dp[j],dp[j-cost[i]]+val[i]); //当j小于cost[i]时,直接沿用原来的值
}
printf("%d
",dp[t]);
return 0;
}
多重背包
每种物品限定了数量。
可以把每一个都看成不同类型的物品,这样就转化成0-1背包问题了。
但是直接转换会导致复杂度飙升,可以利用2进制来优化。
以19为例子 它可以拆分(1+2+4+8+3),其实就是(2^0 + 2^1 + 2^2 + 2^3 +3)
按照上面的划分,可以把19个同类型的背包拆分成5种不同类型的背包,比一个一个拆分要好得多。
洛谷p1776
//https://www.luogu.com.cn/problem/P1776
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
const int maxn = 40005;
int dp[maxn];
int val[maxn<<2];
int wei[maxn<<2];
int main()
{
int n,mw;
scanf("%d%d",&n,&mw); //种类 最大容积
int v,w,m; //价值 重量 数量
int cnt=1;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&v,&w,&m);
for(int j=1;j<=m;j<<=1) //二进制优化
{
val[cnt]=v*j; wei[cnt]=w*j;
m-=j; cnt++;
}
if(m)
{
val[cnt]=v*m; wei[cnt]=w*m,cnt++;
}
}
//for(int i=0;i<maxn;i++) dp[i]=0;
for(int i=1;i<=cnt-1;i++) //变成0-1背包问题
for(int j=mw;j>=wei[i];j--)
dp[j]=max(dp[j],dp[j-wei[i]]+val[i]);
printf("%d
",dp[mw]);
return 0;
}