动态规划之背包
1. 0 1 背包
- 什么是 0 1 背包?
有 n 个物品和容量是 v 的背包,每件物品只能选一次,第 i 件物品的体积是 v[i],价值是 w[i],
求放物品进入背包后,体积不超过,但是价值最大。 - 状态转移方程
dp[i][j]表示从1 到 i 中选择,总体积不超过 j 的最大价值。
然后我们可以思考 dp[i][j] 是从哪里来的?思考 第 i 件物品可以选也可以不选,就有两种情况,
dp[ i ][ j ] = dp[ i - 1 ][ j ]
dp[ i ][ j ] = dp[ i - 1 ][ j - v[ i ] ] + w[ i ]
故 dp[ i ][ j ] = max ( dp[ i - 1 ][ j ] , dp[ i - 1 ][ j - v[ i ] ] + w[ i ] )
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
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][j],f[i - 1][j - v[i]] + w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
- 优化( 一维数组 )
我们再来观察状态方程 dp[ i ][ j ] = max ( dp[ i - 1 ][ j ] , dp[ i - 1 ][ j - v[ i ] ] + w[ i ] ),
dp[ i ][ j ] 只于第 i - 1 层的数据有关,所以我们可以用一维数组,就是控制 i ,一层层来更新直至更新到 n 层,
此时我们要注意我们要从后往前,因为一层层从前往后更新的话,我们用的 dp[ i - 1 ][ j - v[ i ] ] 会在第 i层首先会被更新,就不正确了
而第二层循环我们只需要考虑 j >= v[ i ] 的情况。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
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 = m;j >=v[i];j --)
{
f[j] = max(f[j],f[j - v[i]] + w[i]);
}
cout<<f[m]<<endl;
return 0;
}
1. 完全 背包
- 什么是 完全 背包?
有 n 个物品和容量是 v 的背包,每件物品有无限件可用,第 i 件物品的体积是 v[i],价值是 w[i],
求放物品进入背包后,体积不超过,但是价值最大。 - 状态表示
f[ i ][ j ] 表示前 i 个物品,且总体积不大于 j 的所有选法的最大值 - 状态计算
参考 0 1 背包,思考 f[i][j] 是从哪里来的?第 i 件物品可以选 0 个,1个 .....k个 ,取最大值即可
f [ i ][ j ] = f [ i - 1,j - k * v [ i ] ] + k * w [ i ]
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
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 - v[i] * k] + k * w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
- 优化
f [ i ][ j ] = max(f [ i ][ j ] , f [ i - 1 ][ j - v [ i ] * k ] + k * w [ i ]); k = 0 ,1,2....
f[i][ j - v[ i ] ] 展开发现 f [ i ][ j ] = f[i][ j - v[ i ] ] + w
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
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 = v[i];j <= m;j ++)
f[j] = max(f[j],f[j - v[i]] + w[i]);
cout<<f[m]<<endl;
return 0;
}
3.多重 背包
- 什么是 多重 背包?
有 n 个物品和容量是 v 的背包,每件物品最多有s件,第 i 件物品的体积是 v[i],价值是 w[i],
求放物品进入背包后,体积不超过,但是价值最大。 - 状态表示
f[ i ][ j ] 表示前 i 个物品,且总体积不大于 j 的所有选法的最大值 - 状态计算
参考 0 1 背包,思考 f[i][j] 是从哪里来的?第 i 件物品可以选 0 个,1个 .....k个 ,取最大值即可
f [ i ][ j ] = f [ i - 1,j - k * v [ i ] ] + k * w [ i ] 朴素版本的完全背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;
int v[N],w[N],s[N];
int f[N][N];
int main()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++)
cin>>v[i]>>w[i]>>s[N];
for(int i = 1;i <= n;i ++)
for(int j = 0;j <= m;j ++)
for(int k = 0;k <= s[i] && k* v[i] <= j;k ++)
{
f[i][j] = max(f[i][j],f[i - 1][j - v[i] * k] + k * w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
- 优化:二进制优化
不可以用完全背包的方法了,我们可以展开,会发现最后多出来一项,没办法求解
二进制优化就是转化成0 1 背包的问题,比如某一物品有1023件,我们可以分成若干组,
第一组1,第二组2,第三组4,第四组8····一直到加起来等于1023,如果没办法凑整,到了2 ^ k,但是2 ^ k +1 超出总个数了,
我们就直接计算一个常数,s - 2 ^ k,转化成要不要选某一组的0 1背包的问题。

浙公网安备 33010602011771号