0/1 背包问题
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>//个人 一般习惯写全头文件
using namespace std;
#define N 1010
int f[N];
int m,n;//商品数m 以及最大容纳商品体积n
int v[N],jz[N];//体积以及价值
int main()
{
int i,j;
cin>>m>>n;
// scanf("%d %d",&m,&n);
for(i=1;i<=m;i++)//初始化
{
cin>>v[i]>>jz[i];//输入 体积以及价值
}
for(i=1;i<=m;i++)//遍历 商品
for(j=n;j>=v[i];j--)//f[j] 表示的是 可容纳j 体积时可得到的 最大价值
{ f[j]=max(f[j], f[j-v[i]]+jz[i]);//f 这里考虑的是 是否可以 容纳第i 个商品 若可以f[j-v[i]]+jz[i] 不可以 f[j] 及价值体积均无变化
}
cout<<f[n]<<endl;//最后的f[n] 就表示为 最大 体积为n使得最大价值 ,并不一定 取的商品体积恰好等于 n
return 0;
}
2 完全背包问题 也就是 每件商品可使用的次数无限制
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 1010
int f[N];//表示最大体积为N是的最大价值能是多少
int v,w;
int m,n;
int main()
{
int i,j;
cin>>m>>n;
for(i=0;i<m;i++)
{
cin>>v>>w;//直接输入i件商品的 体积以及价值
for(j=v;j<=n;j++)//每次循环得到的都是 使用重复次使用前i+1个商品的最大价值
f[j]=max( f[j], f[j-v]+w);
//可对比 0/1 背包问题 这里仍然使用更新 f[j] 但是 这里的f[j] 已经是包含(可能 )无限次前i件商品
//j不断地-v就是使用重复次 i件商品 f的数组标表示还可容纳的体积
}
cout<<f[n]<<endl;
}
3 完全背包问题
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define N 1010
int f[N];//表示最大体积为N是的最大价值能是多少
int v,w;
int m,n,s;
int main()
{
int i,j;
cin>>m>>n;
for(i=0;i<m;i++)
{
cin>>v>>w>>s;//直接输入i件商品的 体积以及价值
// //可对比 0/1 背包问题 这里仍然使用更新 f[j] 但是 这里的f[j] 已经是包含(可能 )无限次前i件商品
for(j=n;j>=0;j--)
for(int k=1;k<=s&&k*v<=j;k++)
f[j]=max(f[j],f[j-k*v]+k*w);//与0/1 背包不同的是可以选择 商品个数0--s个所以多了一层循环
// f的数组标表示还可容纳的体积
}
cout<<f[n]<<endl;
}
----------------------------之前的都是数组大概有 1000个
--------------------------------------------当超过1000 如下 就会出现时间超限,如下题
有 NN 种物品和一个容量是 VV 的背包。
第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤10000<N≤1000
0<V≤20000<V≤2000
0<vi,wi,si≤20000<vi,wi,si≤2000
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
--------------------------------------------------
解法:这里另一种写法就是 将 每件商品的 可使用数量用二进制拆分 比如10 可用4位二进制 表示出来,但是10 并非是2^4 (最大表示) 所以每次拆分最后一位应该是 10-1-2-4-8=-4 当减少到 为负数时,那么就应该最后一位为10-1-2-4=3 也就是10可拆分为1 2 4 3 这里使用一个数组来存储拆分项
之后根据拆分项将商品的每件每件的分离,形成0/1背包问题
,比如10件价值为v的商品 拆分为
1件 1*v
1件2*v
1件4*v
1件3*v 问题就转化为0/1背包问题,
代码如下
#include<bits/stdc++.h>
using namespace std;
int main()
{
int N,V,v[1001],w[1001],f[2001],s[1001]
int a[25000],b[25000]; //2的12次方大于2000,也就是说一个数最多可以拆成12个,故数组容量乘12
cin>>N>>V;
memset(f,0,sizeof(f));
for(int i=0;i<N;i++)
cin>>v[i]>>w[i]>>s[i];
int total=0;
for(int i=0;i<N;i++)
{
for(int j=1;j<s[i];j<<=1)//二进制拆分
{
a[total]=j*w[i];//存价值
b[total++]=j*v[i];//存容量
s[i]-=j;
}
if(s[i])//当s[i]>0;
{
a[total]=s[i]*w[i];
b[total++]=s[i]*v[i];
}
}
for(int i=0;i<total;i++)//01背包
for(int j=V;j>=b[i];j--)
f[j]=max(f[j],f[j-b[i]]+a[i]);
cout<<f[V];
return 0;
}