引言
今天主要讲的是背包模型二,主要讲的内容是二维费用背包问题和 01 01 01 背包、完全背包求方案数的问题,再就是一些背包的一个简单应用问题。感觉难点首先就是背景问题,也就是阅读理解能力,然后就是对这个问题进行抽象出一个模型,然后对其进行变换,其实不难,比我想象的要简单点,是自己又对未知的恐惧了吧,其实还好,一下子看懂肯定是不现实的,一两天甚至三天看懂,我觉得我行,那就继续加油吧!
一、二维费用的背包问题
标签:背包问题、DP、模板题
思路:
这道题是一个模板题,上一次讲的 宠物小精灵之收服 其实是这个问题的一个应用,顺序有点反,不够没关系。首先这个问题是一个
01
01
01 背包问题,其次不一样的只是多了一维的体积,其实本质上是跟一维没有任何差别的,就是多了一维,详情见代码。
题目描述:
有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。
每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
输入格式
第一行三个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。
接下来有 N 行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤1000,0<V,M≤100,0<vi,mi≤100,0<wi≤1000
输入样例
4 5 6
1 2 3
2 4 4
3 4 5
4 5 6
输出样例:
8
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 110;
int n, V, M;
int f[N][N];
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> V >> M;
for(int i = 0; i < n; ++i)
{
int v, m, w; cin >> v >> m >> w;
for(int j = V; j >= v; --j)
{
for(int k = M; k >= m; --k)
{
f[j][k] = max(f[j][k], f[j-v][k-m]+w);
}
}
}
cout << f[V][M] << endl;
return 0;
}
二、数字组合
标签:动态规划、01背包问题
思路:
首先打眼一看是一个
01
01
01 背包问题,然后让求和为
M
M
M 的方案数,那么我们就要重新定义状态了,
f
[
i
]
[
j
]
f[i][j]
f[i][j] 代表从前
i
i
i 个物品里选择若干个数和为
j
j
j 的方案数,那么状态转移方程可以得出
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
v
]
f[i][j] = f[i-1][j] + f[i-1][j-v]
f[i][j]=f[i−1][j]+f[i−1][j−v] ,也就是从最后一步划分,第
i
i
i 个物品选或者不选的方案数之和,然后跟据 背包问题 的模板就可以将其简化为一维,其实道理是通的,并且由于状态定义变了,所以初始的状态也要看是否发生变化,该题中我们可以得知
f
[
0
]
[
0
]
=
1
f[0][0] = 1
f[0][0]=1 ,所以简化为一维就是
f
[
0
]
=
1
f[0] = 1
f[0]=1 ,因为什么也不选(空集)也是一种方案,然后其余的就是模板了,详情见代码。
题目描述:
给定 N 个正整数 A1,A2,…,AN,从中选出若干个数,使它们的和为 M,求有多少种选择方案。
输入格式
第一行包含两个整数 N 和 M。
第二行包含 N 个整数,表示 A1,A2,…,AN。
输出格式
包含一个整数,表示可选方案数。
数据范围
1≤N≤100,1≤M≤10000,1≤Ai≤1000,答案保证在 int 范围内。
输入样例:
4 4
1 1 2 2
输出样例:
3
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 1e4+10;
int n, m;
int f[N];
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
f[0] = 1;
for(int i = 0; i < n; ++i)
{
int v; cin >> v;
for(int j = m; j >= v; --j)
{
f[j] += f[j-v];
}
}
cout << f[m] << endl;
return 0;
}
三、庆功会
标签:DP、多重背包问题
思路:
这道题就是一个 多重背包 的一个应用,价格代表体积,价值代表价值,
m
m
m 代表容积,然后就是模板了,详情见代码。值得注意的是,这个
N
N
N 的取值范围是容积的范围,另外
v
[
i
]
,
w
[
i
]
v[i],w[i]
v[i],w[i] 其实就是
n
∗
l
o
g
(
10
)
n * log(10)
n∗log(10) ,给
2000
2000
2000 就行了
题目描述:
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。
期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
输入格式
第一行二个数n,m,其中n代表希望购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可)。
输出格式
一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
数据范围
n≤500,m≤6000,v≤100,w≤1000,s≤10
输入样例:
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
输出样例:
1040
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 6010, M = 2000;
int n, m;
int v[M], w[M];
int f[N], cnt;
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 0; i < n; ++i)
{
int a, b, s; cin >> a >> b >> s;
int k = 1;
while(k <= s)
{
cnt++;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k <<= 1;
}
if(s > 0)
{
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
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;
}
四、买书
标签:DP、完全背包问题、背包问题求方案数
思路:
这道题首先是一个完全背包问题,因为每种书没有限制,然后书的价格代表体积,容积就是总钱数,然后就是状态定义了,第二题说的是
01
01
01 背包求方案数,而这个是完全背包求方案数,其实都大差不差,状态定义
f
[
i
]
[
j
]
f[i][j]
f[i][j] 代表从前
i
i
i 个物品中选择若干个物品总体积恰好为
j
j
j 的方案数,那么状态转移方程就为
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
v
]
f[i][j] = f[i-1][j] + f[i-1][j-v]
f[i][j]=f[i−1][j]+f[i−1][j−v] ,该状态的计算就是由该物品选或者不选体积恰好为
j
j
j 的方案数的和,然后就是把它代入模板中去即可,详情见代码。
题目描述:
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
输入格式
一个整数 n,代表总共钱数。
输出格式
一个整数,代表选择方案种数。
数据范围
0≤n≤1000
输入样例1:
20
输出样例1:
2
输入样例2:
15
输出样例2:
0
输入样例3:
0
输出样例3:
1
示例代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 1010;
int n, m;
int v[4] = {10,20,50,100};
int f[N];
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> m;
f[0] = 1;
for(int i = 0; i < 4; ++i)
{
for(int j = v[i]; j <= m; ++j)
{
f[j] += f[j-v[i]];
}
}
cout << f[m] << endl;
return 0;
}