在让CF的动态规划折磨了小半年后,借着这个寒假,决定重新学习动态规划,这篇博客就给大家介绍一些简单的而且比较经典的入门动态规划问题——背包问题。
背包有九讲,这里我为大家浅谈四讲。
在这里有一个技巧:动态转移方程如果用的是上一层的状态就是从大到小枚举,如果用的是当前状态这从小到大枚举。
01背包(每个物品只有一个,在不超背包最大体积的情况下去选取最大的价值)
点这里点这里点这里
定义f[i][j]:前i个物品在背包容量为j下的最优解
1).当背包容量够的时候,直接判断选不选第i个物品
如果选:f[i][j] = f[i-1][j-w[i]] + v[i](选了第i个物品,总体积就减w[i],注意f[][]的定义,所以最后加v[][])
如果不选:f[i][j] = f[i-1][j]
2).当容量不够时,为前i-1物品的最优解,故f[i][j] = f[i-1][j]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int v[N],w[N],f[N][N];
int main()
{
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j>=v[i]) //当够装的时候
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
/* 一维优化
for(int i = 1; i <= n; i++)
for(int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j-v[i]]+w[i]);
cout << f[m] << endl;
*/
cout<<f[n][m]<<endl;
return 0;
}
一维优化原理:
1. f[i] 仅用到了f[i-1]层,
2. j与j-v[i] 均小于j
3. 从大到小循环的原因是f[i, j]要用f[i - 1, j - v[i]] + w[i]来更新, 从大到小可以保证算f[j]时用到的f[j - v[i]]存储的是f[i - 1, j - v[i]], 而不是f[i, j - v[i]];如果从小到大循环, 那么f[j - v[i]]会在f[j]前被计算出来,那么它就表示f[i, j - v[i]]了。
4一定要把这个01背包的状态转移弄明白了,下面的三个背包都是大同小异,不在重复啰嗦了
----------------------------------------------------------------------------------------------------------------------------------------------------------
完全背包(每个物品有无限个,在不超背包最大体积的情况下去选取最大的价值)
点这里点这里点这里
和01背包的理解基本一样,只不过因为每个物品是无限个,所以要枚举在不超过最大体积的情况下当前物品可以最多放多少个,既f[i-1][j-kv[i]]+kw[i]);
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int v[N],w[N],f[N];
int main()
{
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
//二维
// for(int i=1;i<=n;i++)
// {
// for(int j=0;j<=m;j++)
// {
// for(int k=0;k*v[i]<=j;k++)
// {
// f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
// }
// }
// }
// cout<<f[n][m]<<endl;
for(int i = 1;i<=n;i++)
{
for(int j = v[i];j <= m ;j++)
{
f[j] = max(f[j] , f[j-v[i]]+w[i]);
}
}
cout << f[m];
return 0;
}
----------------------------------------------------------------------------------------------------------------------------------------------------------
多重背包(每个物品有有限个,在不超背包最大体积的情况下去选取最大的价值)
点这里点这里点这里
按照完全背包的想法去看,就是多一个条件,即选的物品有一个限制。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int v[N],w[N],f[N][N],s[N];
int main()
{
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i]>>s[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=0;k*v[i]<=j&&k<=s[i];k++)
{
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
优化:(二进制优化法)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int N,V,v[1001],w[1001],dp[2001],s[1001];
int a[25000],b[25000]; //2的12次方大于2000,也就是说一个数最多可以拆成12个,故数组容量乘12
cin>>N>>V;
memset(dp,0,sizeof(dp));
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--)
dp[j]=max(dp[j],dp[j-b[i]]+a[i]);
cout<<dp[V]<<endl;
return 0;
}
----------------------------------------------------------------------------------------------------------------------------------------------------------
分组背包(每类物品只能选取一个,在不超背包最大体积的情况下去选取最大的价值)
点这里点这里点这里
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N][N];
int v[N][N],w[N][N],s[N];
int n,m,k;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
for(int j=0;j<s[i];j++){
cin>>v[i][j]>>w[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];
for(int k=0;k<s[i];k++)
{
if(j>=v[i][k])
f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[n][m]<<endl;
}
/*优化
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N];
int v[N][N],w[N][N],s[N];
int n,m,k;
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>s[i];
for(int j=0;j<s[i];j++)
{
cin>>v[i][j]>>w[i][j];
}
}
for(int i=0;i<n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=0;k<s[i];k++)
{
if(j>=v[i][k]) f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[m]<<endl;
}
*/
四个背包大体就是这样,如果不理解也不要着急,先把大体的思路记住,抽时间慢慢品味,准有醍醐灌顶的那一刻。