动态规划之完全背包

完全背包的概念

  完全背包的话我们可以抽象成,给我们一个体积为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值