完全背包的概念
完全背包的话我们可以抽象成,给我们一个体积为V的背包,然后给我们n个物品,然后每个物品的体积为v,价值为w,我们对于任意一个物品可以取任意个,问我们在不超过背包容量的情况下我们最多可以取到的最大价值是多少。
母题(完全背包)
完全背包问题
题意:和完全背包抽象出来的概念一样。
思路:
我们通过上面的分析我们可以写出一下的核心代码。
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]);
然后我们分析一下我们得出的状态转移的式子
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
由上两式,可得出如下递推关系:
f[i][j]=max(f[i-1][j], f[i,j-v]+w)
这样的话我们就可以去掉一层循环
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]);
}
}
然后我们又会发现它变的和01背包十分的相似,我们可以通过对01背包的降维方法对它进行降维。然后我们就可以得到最终的代码:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int f[N];
int main()
{
cin.tie(0)->sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int v, w;
cin >> v >> w;
for (int j = v; j <= m; j++)
{
f[j] = max(f[j], f[j - v] + w);
}
}
cout << f[m] << endl;
return 0;
}
终于我们在经历了三次化简之后我们把它变成了两层循环和一维。
例题一(买书)
买书
题意:给我们四本书的价格,然后给我们一个总花费n,让我们求出有多少种花发可以让我们正好花完n。
思路:我们设f[i]为花费正好为i的前提下,有几种合法方案。
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int d[5] = {0, 10, 20, 50, 100};
int f[N];
int main()
{
cin.tie(0)->sync_with_stdio(false);
int n;
cin >> n;
f[0] = 1;
for (int i = 1; i <= 4; i++)
{
for (int j = d[i]; j <= n; j++)
{
f[j] += f[j - d[i]];
}
}
cout << f[n] << endl;
return 0;
}
大家要练习的话可以看看这个题
货币系统一
例题二(货币系统二)
货币系统二
思路:这个题的话我们只需要看一下后面的数是否能被前面的数凑出来,也就是说,把数本身的值看成体积。问恰好能凑出几个数,然后凑不出来的数的个数就是答案。
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int a[N];
int f[N];
int main()
{
cin.tie(0)->sync_with_stdio(false);
int n, m;
int T;
cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a + n);
m = a[n - 1];
memset(f, 0, sizeof f);
f[0] = 1;
int res = 0;
for (int i = 0; i < n; i++)
{
if (!f[a[i]])
res++;
for (int j = a[i]; j <= m; j++)
f[j] += f[j - a[i]];
}
cout << res << endl;
}
return 0;
}