算法学习系列(五十三):背包模型(二)

引言

今天主要讲的是背包模型二,主要讲的内容是二维费用背包问题和 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[i1][j]+f[i1][jv] ,也就是从最后一步划分,第 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) nlog(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[i1][j]+f[i1][jv] ,该状态的计算就是由该物品选或者不选体积恰好为 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;
}
  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lijiachang030718

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值