第十三届蓝桥杯b组国赛dp问题

第十三届蓝桥杯 b 组国赛 d p 问题 \Huge{第十三届蓝桥杯b组国赛dp问题} 第十三届蓝桥杯b组国赛dp问题

刷题的时候发现往年国赛题中有三道dp问题,而且还都是背包问题,正好最近没写过dp,那就简单整理一下,尽量把我思路整理清楚hhh。

关于背包问题,可以查看这篇博客:背包九讲------九种背包问题的算法思路+代码分析-CSDN博客

题目链接:备赛蓝桥杯 - 蓝桥云课 (lanqiao.cn)

文章目录

2022

题意

2022由10个不同的正整数相加得到的方案个数。

思路

我们可以将本问题看作是01背包,那么此时问题可以改为:有编号为 1 , 2 , 3 , . . . , 2022 1,2,3,...,2022 1,2,3,...,2022的2022个物品,每个物品的体积和价值都等于它的编号,因此物品的体积和价值可以看作一个限制条件,然后需要选10个数,作为第二个限制条件。

那么本题的限制条件就有两个,然后呢?

那不就是一道二维费用流背包吗?

众所周知,二维费用流背包就是在01背包的基础上加了一个或多个限制条件,那我们只需将加一层枚举,来枚举体积和个数即可。

标程

cpp 复制代码
void Solved() {//在二维01背包的基础上增加一维,用来枚举所选个数
	long long f[2023][11][2023];
	for(int i = 0; i <= 2022; i ++ ) f[i][0][0] = 1;
		
	for(int i = 1; i <= 2022; i ++ )
		for(int j = 1; j <= 10; j ++ )
			for(int k = 1; k <= 2022; k ++ ) {
				f[i][j][k] = f[i - 1][j][k];
				if(k >= i) {
					f[i][j][k] += f[i - 1][j - 1][k - i];
				}
			}
	cout << f[2022][10][2022] << endl;
}

void Solved() {//优化一维,详细优化思路可以查看上面背包问题的博客
	long long f[11][2023];
	f[0][0] = 1;

	for(int i = 1; i <= 2022; i ++ )
		for(int j = 10; j >= 1; j -- )
			for(int k = i; k <= 2022; k ++ ) {
				f[j][k] += f[j - 1][k - i];		
			}
	cout << f[10][2022] << endl;
}

搬砖

题意

给出n个砖,每块砖对应的有体积和价值。现要求将砖块摞起来,要求每块砖上面的所有砖加起来的重量不超过其自身的价值。求摞起来的最大总价值。

思路

一道背包问题,但是增加了一些限制条件。

我们考虑:每块砖放与不放的状态是由其上面的砖块重量之和决定,那么我们需要从顶部考虑放砖块,才能保证不影响接下来的结果。

跟据贪心的思路:放在顶部的砖块需要是重量较小,且价值较大的砖块。

那么在枚举的过程中,我们需要按"重量较小,且价值较大"的顺序来优先考虑砖块,因此需要排序。

若有编号为1,2的两块石头,那么其重量和价值关系共有两种情况:

  1. w 1 < w 2 , v 1 < v 2 w_1<w_2, ~ v_1<v_2 w1<w2, v1<v2
  2. w 1 < w 2 , v 1 > v 2 w_1<w_2, ~ v_1>v_2 w1<w2, v1>v2

对于第二种情况,毫无疑问1应该放在2上面,对于第二种情况,只能1放在2上面。

因此排序规则为: w i + v i < w j + v j w_i+v_i < w_j+v_j wi+vi<wj+vj。

标程

cpp 复制代码
int n, f[200001], sum, res;
PII a[1005];

void Solved() {
	cin >> n;
	for(int i = 1; i <= n; i ++ ) {
		cin >> a[i].fi >> a[i].se;
		sum += a[i].fi;
	}
    
	sort(a + 1, a + n + 1, [](PII n1, PII n2) {
		return n1.fi + n1.se < n2.fi + n2.se;
	});

	for(int i = 1; i <= n; i ++ ) {
		for(int j = sum; j >= a[i].fi; j -- ) {
			if(j - a[i].fi <= a[i].se) {
				f[j] = max(f[j], f[j - a[i].fi] + a[i].se);
			}
			res = max(res, f[j]);
		}
	}
	
	cout << res << endl;
}

费用报销

题意

给出若干张发票,每个发票有日期(月日)、价值。要求选出若干张发票,且相互日期间隔不小于k,总和不超过m,求能选出的最大总和。

思路

本题有两个限制条件:

  1. 相互日期间隔不小于k
  2. 总和不超过m

那么这道题就比较简单了,我们首先记录每一天最大的发票价值(当天没有发票就为0,有发票就为最大的数目)。

注意:可能会有某天有多张发票,而题目中k大于等于1,因此不可能选择同一天的发票。

然后枚举每一天,并判断是否需要加上当前天的发票。

标程

cpp 复制代码
#define int long long
int today(int mm, int dd) {
	int mon[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31 ,30, 31, 30, 31};
	int sum = dd;
	for(int i = 1; i < mm; i ++ ) sum += mon[i];
	return sum;
}

void Solved() {
    int n, m, k, da[370] = {0};
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i ++ ) {
		int mm, dd, vv; cin >> mm >> dd >> vv;
		int data = today(mm, dd);
		da[data] = max(da[data], vv);
	}
	
	for(int i = 1; i <= 365; i ++ ) {
		int t = max(0ll, i - k);
		if(da[i] + da[t] < m) {
			da[i] = max(da[i - 1], da[i] + da[t]);
		} else {
			da[i] = da[i - 1];
		}
	}
	
	cout << da[365] << endl;
}
相关推荐
2301_795167201 小时前
玩转Rust高级应用 如何避免对空指针做“解引用”操作,在C/C++ 里面就是未定义行为
c语言·c++·rust
智驱力人工智能2 小时前
基于视觉分析的人脸联动使用手机检测系统 智能安全管理新突破 人脸与手机行为联动检测 多模态融合人脸与手机行为分析模型
算法·安全·目标检测·计算机视觉·智能手机·视觉检测·边缘计算
2301_764441333 小时前
水星热演化核幔耦合数值模拟
python·算法·数学建模
循环过三天3 小时前
3.4、Python-集合
开发语言·笔记·python·学习·算法
priority_key5 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
不染尘.6 小时前
2025_11_7_刷题
开发语言·c++·vscode·算法
似水এ᭄往昔6 小时前
【C++】--stack和queue
开发语言·c++
仰望—星空6 小时前
MiniEngine学习笔记 : CommandListManager
c++·windows·笔记·学习·cg·direct3d
来荔枝一大筐7 小时前
力扣 寻找两个正序数组的中位数
算法
算法与编程之美7 小时前
理解Java finalize函数
java·开发语言·jvm·算法