多关键字dp,P1687 机器人小Q

P1687 机器人小Q - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

成功派送完这个大单后,餐厅决定引入一名新成员:机器人小 Q。小 Q 的到来让餐厅的客源增加了不少,但是,一段时间之后,新的问题又出现了,小 Q 和我们可不一样,如果要让他工作的话,我们得给他输入能量以保持体力,而小 Q 的能量菜单表上已经按一定顺序给出了 N 个单位的能量值,但是每个单位的能量由于来源不同,需要消耗一定的时间摄入。已知小 Q 每天充电的时间上限值为 119,如果时间超出的话就会自动崩溃,大家都想让小 Q 留下来,经过研究之后,决定派 HWX 和 XYF 去和老板谈判,考虑角度不一样,LXC 老板才不想听什么辛酸流泪史,他只想知道,若是想让小 Q 获得 k 单位的能量(也就是能量表中可以不接受某些能量)最少需要几天来充电。

输入格式

第一行,两个整数,N,K 分别表示小 Q 的能量菜单上有 N 个单位的能量,想获取其中的 k 个。

第二行,N个整数,分别是第 i 个单位能量需要的充电时间。

输出格式

仅一行,一个整数,为最少需要天数。

如果永远不能达到题目要求,输出 You can't do it.

输入输出样例

输入 #1复制

复制代码
7 3
1 119 119 1 120 120 118

输出 #1复制

复制代码
2

说明/提示

样例解释

只接收 1,1,1181。显然这需要 2 天。

数据规模

对于 30% 的数据,有 1≤K≤N≤20。

对于 100% 的数据,有 1≤K≤N≤3000。

解析:

多关键字dp

我们很容易想到将状态划分为 f[i][j] :表示从前 i 个能量中选取 j 个的最小天数;

但我们发现:每天充电上限为 119 这一状态无法体现,即这个划分方式并非不重不漏的,而且这一状态对天数的影响至关重要;

因此我们可以将这个划分方式稍微修改一下,划分为 f[i][j][1] 和 f[i][j][0];

f[i][j][1] 表示表示从前 i 个能量中选取 j 个的最小天数,f[i][j][0] 表示这种情况下的当天消耗的时间;

(即,以天数为第一关键字,以最后一天充电的时长为第二关键字选取最优解)

这样,我们就将整个问题划分成了多个不重不漏的子集

则状态的转移过程为:

f[i−1][j−1][0]+w[i]>119

这时候我们应该增加新的一天来使用第i 个能量单位

因此比较f[i−1][j−1][1]+1 与f[i][j][1] 的大小关系。

若f[i−1][j−1][1]+1<f[i][j][1]

那么此时选取第 i 个能量单位的决策一定优于f[i][j] 的决策

若f[i][j][1]=f[i−1][j−1][1]+1,f[i][j][0]=w[i]

f[i−1][j−1][1]+1=f[i][j][1],那么这时候比较第二关键字

f[i][j][0]=min(w[i],f[i][j][0])

f[i−1][j−1][0]+w[i]≤119

这时候考虑将第

i 个能量单位追加到最后一天内,仍然按序比较两个关键字

若 f[i−1][j−1][1]<f[i][j][1]

此时选取第 i 个能量单位的决策一定优于 f[i][j] 的决策

f[i][j][1]=f[i−1][j−1][1],f[i][j][0]=f[i−1][j−1][0]+w[i]

若f[i−1][j−1][1]=f[i][j][1]

此时比较第二关键字

f[i][j][0]=min(f[i][j][0],f[i−1][j−1][0]+w[i])

如果我们不选取第 i 个能量单位

这时候直接继承前面的状态f[i][j][0]=f[i−1][j][0],f[i][j][1]=f[i−1][j][1]

cpp 复制代码
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
const int N = 3e3 + 5;
int n, m;
int a[N];
int f[N][N][2];

int main() {
	scanf("%d%d", &n, &m);
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[++cnt]);
		if (a[cnt] > 119)cnt--;
	}
	n = cnt;
	if (cnt < m) cout << "You can't do it." << endl;
	memset(f, 0x3f3f3f3f, sizeof f);
	for (int i = 0; i <= n; i++) {
		f[i][0][1] = 0;
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= min(i,m); j++) {
			f[i][j][1] = f[i - 1][j][1];
			f[i][j][0] = f[i - 1][j][0];
			if (f[i - 1][j - 1][0] + a[i] > 119) {
				if (f[i][j][1] > f[i - 1][j - 1][1] + 1) {
					f[i][j][1] = f[i - 1][j - 1][1] + 1;
					f[i][j][0] = a[i];
				}
				if (f[i][j][1] == f[i - 1][j - 1][1] + 1) {
					f[i][j][0] = min(f[i][j][0], a[i]);
				}
			}
			else {
				if (f[i - 1][j - 1][1] < f[i][j][1]) {
					f[i][j][1] = f[i - 1][j - 1][1];
					f[i][j][0] = f[i - 1][j - 1][0] + a[i];
				}
				if(f[i - 1][j - 1][1] == f[i][j][1]){
					f[i][j][0] = min(f[i][j][0], f[i - 1][j - 1][0] + a[i]);
				}
			}
		}
	}
	cout << f[n][m][1] << endl;
	return 0;
}
相关推荐
Kaltistss41 分钟前
98.验证二叉搜索树
算法·leetcode·职场和发展
知己如祭1 小时前
图论基础(DFS、BFS、拓扑排序)
算法
mit6.8241 小时前
[Cyclone] 哈希算法 | SIMD优化哈希计算 | 大数运算 (Int类)
算法·哈希算法
c++bug1 小时前
动态规划VS记忆化搜索(2)
算法·动态规划
哪 吒1 小时前
2025B卷 - 华为OD机试七日集训第5期 - 按算法分类,由易到难,循序渐进,玩转OD(Python/JS/C/C++)
python·算法·华为od·华为od机试·2025b卷
军训猫猫头2 小时前
1.如何对多个控件进行高效的绑定 C#例子 WPF例子
开发语言·算法·c#·.net
success2 小时前
【爆刷力扣-数组】二分查找 及 衍生题型
算法
Orlando cron2 小时前
数据结构入门:链表
数据结构·算法·链表
牛客企业服务3 小时前
2025年AI面试推荐榜单,数字化招聘转型优选
人工智能·python·算法·面试·职场和发展·金融·求职招聘
糖葫芦君4 小时前
Policy Gradient【强化学习的数学原理】
算法