动态规划入门,从01背包开始!
动态规划
核心 : 状态的表示和 状态的计算
-
状态的表示:需要有几维来表示?每一个状态表示什么?
状态f(i.j):集合 + 属性
集合:所有可选的方案、条件(只从前i个物体中选、总体积小于j)
属性:Max、Min、数量
-
状态的计算:如何一步步把状态算出来?
集合划分的原则:不重不漏
集合的划分——不含i: $f(i-1,j) $ 、含i: \(f(i-1,j-v_i)+w_i\)
f(i,j) = max ( $f(i-1,j) $ ,\(f(i-1,j-v_i)+w_i\) )
背包问题
- 01背包问题:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品最多只能用一次,背包能装得下的情况下,选出物品的总价值最大
- 完全背包:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品可以用无限次,背包能装得下的情况下,选出物品的总价值最大
- 多重背包:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品最多有\(S_i\) 个,背包能装得下的情况下,选出物品的总价值最大
- 分组背包:N组物体,每组物品若干种,每组最多选一个物品,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,背包能装得下的情况下,选出物品的总价值最大
01背包
二维写法:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int v[N];
int w[N];
int 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][j],f[i - 1][j - v[i]]+ w[i]);
}
cout << f[n][m] << endl;
return 0;
}
优化后的一维写法
由观察可得:f(i,j)之和f(i-1,j)以及f(i-1,j-v[i])有关,可优化为一维
注意:内层循环是倒序遍历!防止一个物体被添加多次!
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int v[N];
int w[N];
int 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 = m; j >= v[i]; j--)
{
f[j] = max(f[j],f[j - v[i]]+ w[i]);
}
cout << f[m] << endl;
return 0;
}
完全背包
-
01背包问题每个物品状态分两种,即某物品选or不选
-
完全背包每个每个物品状态分无数种,即某物品选多少个
f[i,j] = Max(f[i-1,j], f[i-1,j-v]+w,f[i-1,j-2v]+2w,f[i-1,j-3v]+3w...)
f[i,j-v] = Max( f[i-1,j-v], f[i-1,j-v]+w , f[i-1,j-v]+2w....)
因此f[i,j]可以简化为max(f[i-1,j],f[i,j-v]+w)
写法与01背包类似,二维数组只需要把f(i-1,j-v[i])改成f(i,j-v[i])
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int v[N];
int w[N];
int 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][j],f[i][j - v[i]]+ w[i]);
}
cout << f[n][m] << endl;
return 0;
}
一维数组法只需要把内层 循环从倒序改为正序
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int v[N];
int w[N];
int 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 = v[i]; j <= m ; j++)
{
f[j] = max(f[j],f[j - v[i]]+ w[i]);
}
cout << f[m] << endl;
return 0;
}

浙公网安备 33010602011771号