01背包方案数
01背包求解方案数时,有下面两种情况:
- 不超过背包容量求方案数
- 恰好装满背包时求方案数
不超过背包容量求方案数
有 N N N件物品和一个容量是 M M M的背包。每件物品只能使用一次。第 i i i件物品的体积是 v i v_i vi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,求总方案数。
输入格式
第一行两个整数, N N N, M M M,用空格隔开,分别表示物品数量和背包容积。
第二行有 N N N 个整数 v i v_i vi用空格隔开,表示第 i i i 件物品的体积。
输出格式
输出一个整数,表示方案数。
样例输入
4 5
2 2 3 7
样例输出
7
提示
0 < N , M ≤ 1000 0<N,M≤1000 0<N,M≤1000
0 < v i ≤ 1000 0<v_i≤1000 0<vi≤1000
算法思想
4 4 4件物品,体积分别为 2 1 、 2 2 、 3 、 7 2_1、2_2、3、7 21、22、3、7,在不超过背包容量 5 5 5的情况下,可以选择的方案有:
- 装 0 0 0件物品, 1 1 1种方案。
- 装 1 1 1件物品: 2 1 2_1 21、 2 2 2_2 22、 3 3 3,有 3 3 3种方案。
- 装 2 2 2件物品: 2 1 / 2 2 2_1/2_2 21/22、 2 1 / 3 2_1/3 21/3、 2 2 / 3 2_2/3 22/3,有 3 3 3种方案。
一共有 7 7 7种方案。
求背包方案数可以通过递推计算。
状态表示
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i件物品在不超过背包容量 j j j的情况下物品选择的方案数
状态计算
从最后一步分析,对于第 i i i件物品,只有两种情况:
- 不选第 i i i件物品,方案数为前 i − 1 i-1 i−1件物品在不超过背包容量 j j j的情况下物品选择的方案数 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]
- 选择第 i i i件物品,方案数为前 i − 1 i-1 i−1件物品在不超过背包容量 j − v i j-v_i j−vi的情况下物品选择的方案数 f [ i − 1 ] [ j − v i ] f[i-1][j-v_i] f[i−1][j−vi]。该情况需要满足 v i ≤ j v_i\le j vi≤j
初始状态
要求的是不超过背包容量 j j j的情况下物品选择的方案数,因此对于任意 j j j都有 f [ 0 ] [ j ] = 1 f[0][j]=1 f[0][j]=1,表示在不超过背包容量 j j j的情况下,不选择任何物品,方案数为 1 1 1。
代码实现
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[N][M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
for(int j = 0; j <= m; j ++) f[0][j] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i - 1][j]; //不装第i件物品的方案数
if(j >= v[i]) f[i][j] += f[i - 1][j - v[i]]; //装第i件物品的方案数
}
cout << f[n][m];
return 0;
}
空间优化
基本思想可以参考博主的另一篇文章——每周一算法:背包问题(一)01背包
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
for(int j = 0; j <= m; j ++) f[j] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = m; j >= v[i]; j --)
{
f[j] += f[j - v[i]]; //装第i件物品的方案数
}
cout << f[m];
return 0;
}
恰好装满背包求方案数
有 N N N件物品和一个容量是 M M M的背包。每件物品只能使用一次。第 i i i件物品的体积是 v i v_i vi。
求解将哪些物品装入背包,可使这些物品恰好装满背包,求总方案数。
输入格式
第一行两个整数, N N N, M M M,用空格隔开,分别表示物品数量和背包容积。
第二行有 N N N 个整数 v i v_i vi用空格隔开,表示第 i i i 件物品的体积。
输出格式
输出一个整数,表示方案数。
样例输入
4 5
2 2 3 7
样例输出
2
提示
0 < N , M ≤ 1000 0<N,M≤1000 0<N,M≤1000
0 < v i ≤ 1000 0<v_i≤1000 0<vi≤1000
算法思想
4 4 4件物品,体积分别为 2 1 、 2 2 、 3 、 7 2_1、2_2、3、7 21、22、3、7,恰好装满容量为 5 5 5的背包可以选择的方案有:
- 装 2 2 2件物品: 2 1 / 3 2_1/3 21/3、 2 2 / 3 2_2/3 22/3,共 2 2 2种方案。
一共有 2 2 2种方案。
求背包方案数可以通过递推计算。
状态表示
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i件物品在恰好装满 j j j的情况下物品选择的方案数
状态计算
从最后一步分析,对于第 i i i件物品,只有两种情况:
- 不选第 i i i件物品,方案数为前 i − 1 i-1 i−1件物品在恰好装满 j j j的情况下物品选择的方案数 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]
- 选择第 i i i件物品,方案数为前 i − 1 i-1 i−1件物品在恰好装满 j − v i j-v_i j−vi的情况下物品选择的方案数 f [ i − 1 ] [ j − v i ] f[i-1][j-v_i] f[i−1][j−vi]。该情况需要满足 v i ≤ j v_i\le j vi≤j
初始状态
要求的是恰好装满容量 j j j的情况下物品选择的方案数,因此只有在 j = 0 j=0 j=0的情况下,有 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1,表示在没有选择任何物品、且背包容量为 0 0 0的情况下,方案数为 1 1 1。
代码实现
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[N][M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
f[0][0] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i - 1][j]; //不装第i件物品的方案数
if(j >= v[i]) f[i][j] += f[i - 1][j - v[i]]; //装第i件物品的方案数
}
cout << f[n][m];
return 0;
}
空间优化
基本思想可以参考博主的另一篇文章——每周一算法:背包问题(一)01背包
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
f[0] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = m; j >= v[i]; j --)
{
f[j] += f[j - v[i]]; //装第i件物品的方案数
}
cout << f[m];
return 0;
}
小结
01背包方案数时,有下面两种情况,计算时的区别仅在于初始状态:
- 不超过背包容量求方案数,初始状态 f [ 0 ] [ j ] = 1 , 0 ≤ j ≤ m f[0][j]=1, 0\le j\le m f[0][j]=1,0≤j≤m
- 恰好装满背包时求方案数,初始状态 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1
完全背包方案数
完全背包(每种物品有无限件)求解方案数时,也有下面两种情况:
- 不超过背包容量求方案数
- 恰好装满背包时求方案数
不超过背包容量求方案数
有 N N N种物品和一个容量是 M M M的背包。每种物品都有无限件可用。第 i i i种物品的体积是 v i v_i vi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,求总方案数。
输入格式
第一行两个整数, N N N, M M M,用空格隔开,分别表示物品数量和背包容积。
第二行有 N N N 个整数 v i v_i vi用空格隔开,表示第 i i i 件物品的体积。
输出格式
输出一个整数,表示方案数。
样例输入
4 5
2 2 3 7
样例输出
9
提示
0 < N , M ≤ 1000 0<N,M≤1000 0<N,M≤1000
0 < v i ≤ 1000 0<v_i≤1000 0<vi≤1000
算法思想
4 4 4种物品,体积分别为 2 1 、 2 2 、 3 、 7 2_1、2_2、3、7 21、22、3、7,在不超过背包容量 5 5 5的情况下,由于每种物品有无限件可选,那么选择的方案有:
- 装 0 0 0件物品, 1 1 1种方案。
- 装 1 1 1件物品: 2 1 2_1 21、 2 2 2_2 22、 3 3 3,有 3 3 3种方案。
- 装 2 2 2件物品: 2 1 / 2 1 2_1/2_1 21/21、 2 1 / 2 2 2_1/2_2 21/22、 2 2 / 2 2 2_2/2_2 22/22、 2 1 / 3 2_1/3 21/3、 2 2 / 3 2_2/3 22/3,有 5 5 5种方案。
一共有 9 9 9种方案。
状态表示
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i种物品在不超过背包容量 j j j的情况下物品选择的方案数
状态计算
从最后一步分析,对于第 i i i种物品,有下面几种情况:
- 不选第 i i i种物品,方案数为前 i − 1 i-1 i−1种物品在不超过背包容量 j j j的情况下物品选择的方案数 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]
- 选择第
i
i
i种物品:
- 选择 1 1 1件,方案数为前 i − 1 i-1 i−1种物品在不超过背包容量 j − v i j-v_i j−vi的情况下物品选择的方案数 f [ i − 1 ] [ j − v i ] f[i-1][j-v_i] f[i−1][j−vi]
- 选择 2 2 2件,方案数为前 i − 1 i-1 i−1种物品在不超过背包容量 j − 2 × v i j-2\times v_i j−2×vi的情况下物品选择的方案数 f [ i − 1 ] [ j − 2 × v i ] f[i-1][j-2\times v_i] f[i−1][j−2×vi]
- …
- 选择最多 k k k件,方案数为前 i − 1 i-1 i−1种物品在不超过背包容量 j − k × v i j-k\times v_i j−k×vi的情况下物品选择的方案数 f [ i − 1 ] [ j − k × v i ] f[i-1][j-k\times v_i] f[i−1][j−k×vi]
因此, f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − v i ] + f [ i − 1 ] [ j − 2 × v i ] + f [ i − 1 ] [ j − 3 × v i ] + . . . + f [ i − 1 ] [ j − k × v i ] f[i][j]=f[i-1][j]+f[i-1][j-v_i]+f[i-1][j-2\times v_i]+f[i-1][j-3\times v_i]+...+f[i-1][j-k\times v_i] f[i][j]=f[i−1][j]+f[i−1][j−vi]+f[i−1][j−2×vi]+f[i−1][j−3×vi]+...+f[i−1][j−k×vi],
而 f [ i ] [ j − v ] = f [ i − 1 ] [ j − v i ] + f [ i − 1 ] [ j − 2 × v i ] + f [ i − 1 ] [ j − 3 × v i ] + . . . + f [ i − 1 ] [ j − k × v i ] f[i][j-v]=f[i-1][j-v_i]+f[i-1][j-2\times v_i]+f[i-1][j-3\times v_i]+...+f[i-1][j-k\times v_i] f[i][j−v]=f[i−1][j−vi]+f[i−1][j−2×vi]+f[i−1][j−3×vi]+...+f[i−1][j−k×vi]
那么, f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − v ] f[i][j]=f[i-1][j]+f[i][j-v] f[i][j]=f[i−1][j]+f[i][j−v]。
初始状态
要求的是不超过容量 j j j的情况下物品选择的方案数,因此对于任意 j j j都有 f [ 0 ] [ j ] = 1 f[0][j]=1 f[0][j]=1,表示在不超过背包容量 j j j的情况下,不选择任何物品,方案数为 1 1 1。
代码实现
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[N][M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
for(int j = 0; j <= m; j ++) f[0][j] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i - 1][j]; //不装第i种物品的方案数
if(j >= v[i]) f[i][j] += f[i][j - v[i]]; //装第i种物品的方案数
}
cout << f[n][m];
return 0;
}
空间优化
基本思想可以参考博主的另一篇文章——每周一算法:背包问题(二)完全背包
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
for(int j = 0; j <= m; j ++) f[j] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = v[i]; j <= m; j ++)
{
f[j] += f[j - v[i]]; //装第i种物品的方案数
}
cout << f[m];
return 0;
}
恰好装满背包求方案数
有 N N N种物品和一个容量是 M M M的背包。每种物品都有无限件可用。第 i i i种物品的体积是 v i v_i vi。
求解将哪些物品装入背包,可使这些物品恰好装满背包,求总方案数。
输入格式
第一行两个整数, N N N, M M M,用空格隔开,分别表示物品数量和背包容积。
第二行有 N N N 个整数 v i v_i vi用空格隔开,表示第 i i i 种物品的体积。
输出格式
输出一个整数,表示方案数。
样例输入
4 5
2 2 3 7
样例输出
2
提示
0 < N , M ≤ 1000 0<N,M≤1000 0<N,M≤1000
0 < v i ≤ 1000 0<v_i≤1000 0<vi≤1000
算法思想
4 4 4种物品,体积分别为 2 1 、 2 2 、 3 、 7 2_1、2_2、3、7 21、22、3、7,恰好装满容量为 5 5 5的背包可以选择的方案有:
- 装 2 2 2件物品: 2 1 / 3 2_1/3 21/3、 2 2 / 3 2_2/3 22/3,共 2 2 2种方案。
一共有 2 2 2种方案。
状态表示
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i种物品在恰好装满 j j j的情况下物品选择的方案数
状态计算
从最后一步分析,对于第 i i i种物品,有下面几种情况:
- 不选第 i i i种物品,方案数为前 i − 1 i-1 i−1种物品在恰好装满 j j j的情况下物品选择的方案数 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]
- 选择第
i
i
i种物品:
- 选择 1 1 1件,方案数为前 i − 1 i-1 i−1种物品在恰好装满 j − v i j-v_i j−vi的情况下物品选择的方案数 f [ i − 1 ] [ j − v i ] f[i-1][j-v_i] f[i−1][j−vi]
- 选择 2 2 2件,方案数为前 i − 1 i-1 i−1种物品在恰好装满 j − 2 × v i j-2\times v_i j−2×vi的情况下物品选择的方案数 f [ i − 1 ] [ j − 2 × v i ] f[i-1][j-2\times v_i] f[i−1][j−2×vi]
- …
- 选择最多 k k k件,方案数为前 i − 1 i-1 i−1种物品在恰好装满 j − k × v i j-k\times v_i j−k×vi的情况下物品选择的方案数 f [ i − 1 ] [ j − k × v i ] f[i-1][j-k\times v_i] f[i−1][j−k×vi]
因此, f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − v i ] + f [ i − 1 ] [ j − 2 × v i ] + f [ i − 1 ] [ j − 3 × v i ] + . . . + f [ i − 1 ] [ j − k × v i ] f[i][j]=f[i-1][j]+f[i-1][j-v_i]+f[i-1][j-2\times v_i]+f[i-1][j-3\times v_i]+...+f[i-1][j-k\times v_i] f[i][j]=f[i−1][j]+f[i−1][j−vi]+f[i−1][j−2×vi]+f[i−1][j−3×vi]+...+f[i−1][j−k×vi],
而 f [ i ] [ j − v ] = f [ i − 1 ] [ j − v i ] + f [ i − 1 ] [ j − 2 × v i ] + f [ i − 1 ] [ j − 3 × v i ] + . . . + f [ i − 1 ] [ j − k × v i ] f[i][j-v]=f[i-1][j-v_i]+f[i-1][j-2\times v_i]+f[i-1][j-3\times v_i]+...+f[i-1][j-k\times v_i] f[i][j−v]=f[i−1][j−vi]+f[i−1][j−2×vi]+f[i−1][j−3×vi]+...+f[i−1][j−k×vi]
那么, f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − v ] f[i][j]=f[i-1][j]+f[i][j-v] f[i][j]=f[i−1][j]+f[i][j−v]。
初始状态
要求的是恰好装满容量 j j j的情况下物品选择的方案数,因此只有在 j = 0 j=0 j=0的情况下,有 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1,表示在没有选择任何物品、且背包容量为 0 0 0的情况下,方案数为 1 1 1。
代码实现
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[N][M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
f[0][0] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i - 1][j]; //不装第i种物品的方案数
if(j >= v[i]) f[i][j] += f[i][j - v[i]]; //装第i种物品的方案数
}
cout << f[n][m];
return 0;
}
空间优化
基本思想可以参考博主的另一篇文章——每周一算法:背包问题(二)完全背包
#include <iostream>
using namespace std;
const int N = 1010, M = 1010;
int v[N], f[M];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i];
//初始状态
f[0] = 1;
//状态计算
for(int i = 1; i <= n; i ++)
for(int j = v[i]; j <= m; j ++)
{
f[j] += f[j - v[i]]; //装第i种物品的方案数
}
cout << f[m];
return 0;
}
小结
完全背包方案数时,也有下面两种情况,计算时的区别仅在于初始状态:
- 不超过背包容量求方案数,初始状态 f [ 0 ] [ j ] = 1 , 0 ≤ j ≤ m f[0][j]=1, 0\le j\le m f[0][j]=1,0≤j≤m
- 恰好装满背包时求方案数,初始状态 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1