1.01背包
放和不放,每种物品只能用一次
#include<iostream>
using namespace std;
#define N 1005
int dp[N];
int main()
{
int n,m,v,w;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v>>w;
for(int j=m;j>=v;j--)
{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
cout<<dp[m]<<endl;
return 0;
}
[01扩展背包——板子]
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,x;
int a[1000];
int b[1000];
int c[1000];
int dp[2000][2000];
int main(){
cin>>n>>m>>x;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=b[i];j--)
{
for(int k=x;k>=c[i];k--)
{
dp[j][k]=max(dp[j][k],dp[j-b[i]][k-c[i]]+a[i]);
}
}
}
cout<<dp[m][x];
}
(https://www.luogu.com.cn/problem/P1910#submit)
2.完全背包
每种物品可无限次使用
从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为F[i] = max(F[i],dp[F-c[i]]+v[i])。
#include<iostream>
using namespace std;
#define N 1005
int dp[N];
int main()
{
int n,m,w,v;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v>>w;
for(int j=v;j<=m;j++)
{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
cout<<dp[m]<<endl;
return 0;
}
3.多重背包例题
多重背包是01背包的扩展,与完全背包不同的是考虑每种物品的数量(不再是无限)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
int dp[100];//dp[i]:取i个物品的最大价值
int v[100];//体积
int w[100];//价值
int num[100];//数量
int m,n;
int main(){
cin>>n>>m;//物品种数和背包zl
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i]>>num[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
for(int k=1;k<=num[i];k++)
{
if(j>=k*v[i])
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<dp[m];
}
4.分组背包P1757 通天之分组背包
不再告诉你每种物品具体有几种,而是告诉你该物品属于那个组
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
int m,n;
int a[2000];
int b[2000];
int c[2000];
int dp[2000];
int main(){
int zu=-1;
cin>>m>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i]>>c[i];
zu=max(zu,c[i]);
}
for(int i=1;i<=zu;i++)//穷举组
{
for(int j=m;j>=0;j--)
{
for(int k=1;k<=n;k++)//穷举每件物品
{
if(c[k]!=i||j<a[k]) continue;
dp[j]=max(dp[j],dp[j-a[k]]+b[k]);
}
}
}
cout<<dp[m];
}
5.背包求方案数
小a点菜
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int a[105];
int dp[10005];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=a[i];j--)
{
dp[j]+=dp[j-a[i]];
}
}
cout<<dp[m];
}`
#include <bits/stdc++.h>//万能头文件
using namespace std;
int prime[201], c = 1, f[201], n;
bool p[201];
int main(){
for ( int i = 2; i <= 200; i++ ){
if ( !p[i] ){
prime[c++] = i;
for ( int j = i*2; j <= 400; j += i )
p[j] = true;
}
}//筛法求素数。主要原则:默认所有数为 0 (false) ,如果碰到一个数是 0 ,就把它 N 以内的所有倍数标记成 1 ,并将这个数存进 prime 数组。(#废话
f[0] = 1;//初值憋忘了。
for ( int i = 1; i < c; i++ )
for ( int j = prime[i]; j <= 200; j++ )
f[j] += f[j-prime[i]];//状态转移2333
while ( cin >> n )//每当输入
printf ( "%d\n", f[n] );//输出对应的答案
return 0;//华丽落幕
}
6.布尔型01背包处理均分(找一半)问题
P1489 猫狗大战
#include<bits/stdc++.h>
using namespace std;
int n;
int sum;
int a[205];
bool dp[2500][2500];//dp[i][j]:i个人的血量和j
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];sum+=a[i];
}
dp[0][0]=true;
for(int i=1;i<=n;i++)
{
for(int j=n/2;j>=0;j--)//找一半的人数
{
for(int k=sum/2;k>=0;k--)//类似于01背包的最大承重
{
if(dp[j][k]==true)
{
dp[j+1][k+a[i]]=true;
}
}
}
}
for(int i=sum/2;i>=0;i--)//从大到小——》找最大值
{
if(dp[n/2][i]==true)
{
cout<<i<<" "<<sum-i;
return 0;
}
}
}
这个问题的模板是ZOJ1149——Dividing
dp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=sum/2;j>=b[i];j--)
{
if(dp[j-b[i]])
{
dp[j]=1;
}
}
}
if(dp[sum/2]==1) yes