首先,这是一道典型的背包模板题(可以从比赛名称看出来(逃)
背包是线性dp中一类较为重要而又特殊的模型,大多都是有着固定的套路在里面.主要分为01背包,多重背包,完全背包,这三类的区别十分明显,01背包是单个物品只能取一次,多重背包是单个物品可以取有限次,而完全背包则可以取无限次,根据取的次数不同,三种背包的核心代码也会稍有不同.除此之外就是这三类的各种混搭变形与优化,难度都不是特别大,只要可以保证足够的刷题量就可以将这个模块吃下.所以对于还不熟悉这个套路的人,蒟蒻在这里列出来一个题单(仅供参考
简单题&经典题
- luogu P1048 采药
- luogu P1060 开心的金明
- luogu P1064 金明的预算方案
(这三道都是往年的普及组测试题,比较简单,但都很基础
进阶题
- luogu P1273 & 本书0x54中的选课 都是将树形dp与背包相结合
- luogu P1776 宝物筛选_NOI导刊2010提高(02)是需要单调队列优化的多重背包
- luogu P1941 飞扬的小鸟 属于背包混搭加一点特判
下面我们进入正题QAQ
在知道解题所用方法时,我们就可以再通过题面很轻易的看出这是个01背包(因为每个物品只能使用一次
01背包问题的一般模型如下(或者说是所有01背包的潜台词??:
给定n个物品,其中第i个物品体积为vi,价值为wi,有一个容量为M的背包,要求将一些物品放入背包内,保证在总体积不超过M的情况下使得物品的总价值最大qaq
一般情况下,01背包的模板都是如下代码所示:
for(int i=1;i<=m;i++){ for(int j=n;j>=tim[i];j--){ //内部为你需要执行的操作 } }
请注意,以上代码的第二重for循环我们用了倒序,这个属于01背包一大难点,具体证明请自行翻书(逃
至于本题,很明显可以看出N是n个物品,M为背包容积,由于题目要求的是和为j是的方案数,所以我们很容易得出状态转移方程:f[j] += f[j-a[i]];
此时我们回头看题,题目要求中(1<N<100)和(1<M<10000)
而01背包的复杂度为O(nm),可以轻松跑过,于是我们就可以开开心心的把01背包模板打上去了qaq
code
#include <bits/stdc++.h> #define ll long long const int N = 110; using namespace std; ll n, m; int a[N], f[N]; int main () { cin >> n >> m; for (register int i = 1; i <= n; i++) { cin >> a[i]; } f[0] = 1; for (register int i = 1; i <= n; i++) { for (register int j = m; j >= a[i]; j--) { f[j] += f[j-a[i]]; } } cout << f[m] << endl; return 0; }
最后,给挣扎在背包苦海中的大佬们一个福利
https://www.kancloud.cn/kancloud/pack/70124 (由于没有超链接这个功能,只能将网址发出来了,这是一位真·巨佬写的背包九讲,请各位放心食用)