1.0/1背包
题目描述
给一个能承重V的背包,和n件物品,我们用重量和价值的二元组来表示一个物品,第i件物品表示为(Vi,Wi),问:在背包不超重的情况下,得到物品的最大价值是多少?
输入
第一行输入两个数 V,n,分别代表背包的最大承重和物品数。
接下来n行,每行两个数Vi,Wi,分别代表第i件物品的重量和价值。
(Vi≤V≤10000,n≤100,Wi≤1000000)
输出
输出一个整数,代表在背包不超重情况下所装物品的最大价值。
样例输入1
15 4
4 10
3 7
12 12
9 8
样例输出1
19
动归状态 dp[ i ][ j ]:前i件物品在背包承重为j的情况下的最大价值。
动归方程 dp[ i ][ j ]=max(dp[ i-1 ][ j-v[ i ] ]+w[ i ],dp[ i-1 ][ j ])。
代码实现1(基础版本)
#include<iostream>
using namespace std;
#define MAX_V 10000
#define MAX_N 100
int dp[MAX_N+5][MAX_V+5]={0};
int main()
{
int V,n;
cin>>V>>n;
for(int i=1,vi,wi;i<=n;i++)
{
cin>>vi>>wi;
for(int j=1;j<=V;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=vi)dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi);
}
}
cout<<dp[n][V];
return 0;
}
代码实现2(滚动数组)
#include<iostream>
using namespace std;
#define MAX_V 10000
#define MAX_N 100
int dp[2][MAX_V+5]={0};
int main()
{
int V,n;
cin>>V>>n;
for(int i=1,vi,wi;i<=n;i++)
{
cin>>vi>>wi;
for(int j=1;j<=V;j++)
{
dp[i%2][j]=dp[1-i%2][j];
if(j>=vi)dp[i%2][j]=max(dp[i%2][j],dp[1-i%2][j-vi]+wi);
}
}
cout<<dp[n%2][V];
return 0;
}
代码实现3(一维数组)
#include<iostream>
using namespace std;
#define MAX_V 10000
#define MAX_N 100
int dp[MAX_V+5]={0};
int main()
{
int V,n;
cin>>V>>n;
for(int i=1,vi,wi;i<=n;i++)
{
cin>>vi>>wi;
for(int j=V;j>=vi;j--)//注意这里要从后向前
{
dp[j]=max(dp[j],dp[j-vi]+wi);
}
}
cout<<dp[V];
return 0;
}
2.完全背包
题目描述
有N种物品和一个容量为 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是Ci,价值是Wi。求解在不超过背包容量的情况下,能够获得的最大价值。
输入
第一行为两个整数N、V(1≤N,V≤10000),分别代表题目描述中的物品种类数量N和背包容量V。
后跟N行,第 i 行两个整数Ci、Vi,分别代表每种物品的体积和价值。
输出
输出一个整数,代表可获得的最大价值。
样例输入
5 20
2 3
3 4
10 9
5 2
11 11
样例输出
30
动归状态 dp[ i ][ j ]:前i种物品在背包承重为j的情况下的最大价值。
动归方程 dp[ i ][ j ]=max(dp[ i ][ j-v[ i ] ]+w[ i ],dp[ i-1 ][ j ])。
代码实现
#include<iostream>
using namespace std;
#define MAXSIZE 10000
int dp[MAXSIZE+5]={0};
int main()
{
int N,V;
cin>>N>>V;
for(int i=1,ci,wi;i<=N;i++)
{
cin>>ci>>wi;
for(int j=ci;j<=V;j++)//注意此时正向刷表,和0/1背包区分
{
dp[j]=max(dp[j],dp[j-ci]+wi);
}
}
cout<<dp[V];
return 0;
}
3.多重背包
题目描述
给有一个能承重 V 的背包,和n种物品,每种物品的数量有限多,我们用重量、价值和数量的三元组来表示一个物品,第 i 件物品表示为(Vi,Wi,Si),问在背包不超重的情况下,得到物品的最大价值是多少?
输入
第一行输入两个数V、n,分别代表背包的最大承重和物品种类数。
接下来 n 行,每行三个数 Vi、Wi、Si,分别代表第 i 种物品的重量、价值和数量。
输出
输出一个整数,代表在背包不超重情况下所装物品的最大价值。
样例输入1
15 4
4 10 5
3 7 4
12 12 2
9 8 7
样例输出1
37
优化1-拆分优化
在原先的做法种中,我们依次枚举了以一种物品中的每一个,但是这种效率比较低,我们可以参考二进制,例如一个数14,我们可以将其拆分为1,2,4,8,7四部分,他们可以组合为任意数,这样一来,我们之前要进行的的14次枚举现在只需要4次。
代码实现
#include<iostream>
using namespace std;
#define MAX_N 100000
int dp[MAX_N+5];
int main()
{
int V,n;
cin>>V>>n;
for(int i=1,v,w,s;i<=n;i++)
{
cin>>v>>w>>s;
for(int k=1;s;s-=k,k*=2)
{
k=min(k,s);
for(int j=V;j>=k*v;j--)
{
dp[j]=max(dp[j],dp[j-k*v]+k*w);
}
}
}
cout<<dp[V];
return 0;
}
优化2-单调队列
#include<iostream>
#include<deque>
using namespace std;
#define MAX_V 100000
#define MAX_N 100
int dp[MAX_N+5][MAX_V+5];
int main()
{
int V,n;
cin>>V>>n;
for(int i=1,v,w,s;i<=n;i++)
{
cin>>v>>w>>s;
for(int j=0;j<v;j++)
{
deque<int>q;
for(int k=j;k<=V;k+=v)
{
dp[i-1][k]-=k/v*w;
while(!q.empty()&&dp[i-1][q.back()]<dp[i-1][k])q.pop_back();
q.push_back(k);
if((k-q.front())/v>s)q.pop_front();
dp[i][k]=dp[i-1][q.front()]+k/v*w;
}
}
}
cout<<dp[n][V];
return 0;
}
4.分组背包
题目背景
直达通天路·小 A 历险记第二篇
题目描述
自 01 01 01 背包问世之后,小 A 对此深感兴趣。一天,小 A 去远游,却发现他的背包不同于 01 01 01 背包,他的物品大致可分为 k k k 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。
输入格式
两个数 m , n m,n m,n,表示一共有 n n n 件物品,总重量为 m m m。
接下来 n n n 行,每行 3 3 3 个数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,表示物品的重量,利用价值,所属组数。
输出格式
一个数,最大的利用价值。
样例 #1
样例输入 #1
45 3
10 10 1
10 5 1
50 400 2
样例输出 #1
10
提示
0
≤
m
≤
1000
0 \leq m \leq 1000
0≤m≤1000,
1
≤
n
≤
1000
1 \leq n \leq 1000
1≤n≤1000,
1
≤
k
≤
100
1\leq k\leq 100
1≤k≤100,
a
i
,
b
i
,
c
i
a_i, b_i, c_i
ai,bi,ci 在 int
范围内。
dp[i][j]:前i个分组在容量为觉得情况下的最大价值
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w)
#include<bits/stdc++.h>
using namespace std;
int v,n,t;
int x;
int g[205][205];
int i,j,k;
int w[10001],z[10001];
int b[10001];
int dp[10001];
int main(){
cin>>v>>n;
for(i=1;i<=n;i++){
cin>>w[i]>>z[i]>>x;
t=max(t,x);
b[x]++;
g[x][b[x]]=i;
}
for(i=1;i<=t;i++){
for(j=v;j>=0;j--){
for(k=1;k<=b[i];k++){
if(j>=w[g[i][k]]){
dp[j]=max(dp[j],dp[j-w[g[i][k]]]+z[g[i][k]]);
}
}
}
}
cout<<dp[v];
return 0;
}