背包问题感觉主要还是01背包和完全背包,其他都花里胡哨orz
※※※最根本的状态转移思路:f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])
※※※也就是说当我们从头到尾遍历每一个物品,对于每一个物品都有装or不装两种,在背包容量固定的情况下(这里的j,看成容量上限),是否选用这个物品,如果选用,转移到[j-w[i]]这个容量的时候,不选这个物品,就直接看下一个。
注意,不管选不选这个物品,容量上限都是不变的,我们可以依此进行状态转移。
资料:
背包九讲
经典背包九讲,这篇博客还带了自己的一些理解
背包六问
力扣上的背包六问,题型还是很经典的
模板
//01背包模板(+优化)
for (int i = 1; i <= n; i++)
for (int j = V; j >= w[i]; j--)//逆序
f[j] = max(f[j], f[j - w[i]] + v[i]);
//完全背包模板(+优化)
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= V; j++)//顺序
f[j] = max(f[j], f[j - w[i]] + v[i]);
//多重背包,我觉得有点用到倍增的思想
for (int i = 1; i <= n; i++) {
int num = min(p[i], V / w[i]);
for (int k = 1; num > 0; k <<= 1) {
if (k > num) k = num;
num -= k;
for (int j = V; j >= w[i] * k; j--)
f[j] = max(f[j], f[j - w[i] * k] + v[i] * k);
}
}
例题
01背包-洛谷P1164-小A点菜
(01背包)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[101],f[101][10001]={0};
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(j==a[i])f[i][j]=f[i-1][j]+1;//1代表刚好可以买这道菜
if(j>a[i]) f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
//f[i-1][j]代表不选这个菜也能恰好到j,而f[i-1][j-a[i]]就是选这道菜能到j这个价格。注意如果选这道菜能到这个价格没有方案书那么前一个f[i-1][j-a[i]]就是0了
if(j<a[i]) f[i][j]=f[i-1][j];//不能选这道菜因为超出价格了,就沿袭悬赏一道菜的时候
}
cout<<f[n][m];//选第n到菜的时候,花m元的种类数
return 0;
}
写成一维:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
int v[maxn],f[maxn];
int main(){
int n,m;
cin>>n>>m;
f[0]=1;
for(int i=1;i<=n;++i)
cin>>v[i];//读入 价值
for(int i=1;i<=n;++i)
for(int j=m;j>=v[i];--j)
f[j]+=f[j-v[i]];//现在的花费+=我不点这个菜的时候的花费
cout<<f[m]<<endl;最后把最后一个点的花费输出来就可以了
return 0;
}
完全背包模板题-洛谷p1616
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是 LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:
-
每种草药可以无限制地疯狂采摘。
-
药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
#include <cstdio>
#include <algorithm>
using namespace std;
int i,j,T,M,t[10001],p[10001],f[10001][100001];
int main()
{
scanf("%d%d",&T,&M);
for(i=1;i<=M;i++)
scanf("%d%d",&t[i],&p[i]);
for(i=1;i<=M;i++)
{
//从左到右
for(j=0;j<t[i];j++)
f[i][j]=f[i-1][j];//针对左边为数组越界的状态
for(j=t[i];j<=T;j++)
f[i][j]=max(f[i-1][j],f[i][j-t[i]]+p[i]);//递推
}
printf("%d",f[M][T]);
return 0;
}
一维
#include <cstdio>
#include <algorithm>
using namespace std;
int i,j,T,M,t[10001],p[10001],f[100001];
int main()
{
scanf("%d%d",&T,&M);
for(i=1;i<=M;i++)
scanf("%d%d",&t[i],&p[i]);
for(i=1;i<=M;i++)
for(j=t[i];j<=T;j++)//当需要的左边的值为数组越界时,值不变
f[j]=max(f[j],f[j-t[i]]+p[i]);//递推
printf("%d",f[T]);
return 0;
}