C. Messenger in MAC
C. Messenger in MAC
In the new messenger for the students of the Master's Assistance Center, Keftemerum, an update is planned, in which developers want to optimize the set of messages shown to the user. There are a total of messages. Each message is characterized by two integers and . The time spent reading the set of messages with numbers (, all are distinct) is calculated by the formula:
Note that the time to read a set of messages consisting of one message with number is equal to . Also, the time to read an empty set of messages is considered to be .
The user can determine the time that he is willing to spend in the messenger. The messenger must inform the user of the maximum possible size of the set of messages, the reading time of which does not exceed . Note that the maximum size of the set of messages can be equal to .
The developers of the popular messenger failed to implement this function, so they asked you to solve this problem.
Input
Each test consists of multiple test cases. The first line contains a single integer () — the number of test cases. The description of the test cases follows.
The first line of each test case contains two integers and (, ) — the number of messages and the time the user is willing to spend in the messenger.
The -th of the next lines contains two integers and () — characteristics of the -th message.
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, output a single integer — the maximum possible size of a set of messages, the reading time of which does not exceed .
Example
input
5
5 8
4 3
1 5
2 4
4 3
2 3
1 6
4 10
3 12
4 8
2 1
2 12
5 26
24 7
8 28
30 22
3 8
17 17
5 14
15 3
1000000000 998244353
179 239
228 1337
993 1007
output
3
1
2
1
0
Note
In the first test case, you can take a set of three messages with numbers , , and . The time spent reading this set is equal to .
In the second test case, you can take a set of one message with number . The time spent reading this set is equal to .
In the fifth test case, it can be shown that there is no such non-empty set of messages, the reading time of which does not exceed .
解题思路
赛时是用 dp 做的,看了下官方题解貌似还可以暴力贪心。
首先对于任意一组选出的方案 ,当 时, 的部分一定有最小代价 。
这里给出简单证明,当 有序时,。如果 不是有序,那么一定存在两个相邻元素前者比后者大,交换 和 ,此时 。因此当相邻元素都是有序时,代价最小。
假设现在数对 是按照 从小到大排序的。定义 表示从前 个数对中恰好选择了 个,且一定选择第 个数对的方案中代价最小值。根据上一个选择的数对进行状态划分,转移方程就是 。
显然直接暴力转移的话时间复杂度是 。可以发现对于固定 ,每次转移都是求前缀 中 的最小值,因此在枚举到 时依次遍历 ,并用 来维护前缀 中 的最小值。这样转移方程就变成了 。
最后暴力枚举 的所有状态,如果 那么将答案与 取最大值。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2010;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int a[N], b[N], p[N];
LL f[N][N], g[N];
void solve() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d %d", a + i, b + i);
p[i] = i;
}
sort(p + 1, p + n + 1, [&](int i, int j) {
return b[i] < b[j];
});
for (int i = 1; i <= n; i++) {
f[0][i] = f[i][0] = INF;
}
for (int i = 1; i <= n; i++) {
g[0] = f[i - 1][0];
for (int j = 1; j <= n; j++) {
g[j] = min(g[j - 1], f[i - 1][j] - b[p[j]]);
}
for (int j = 1; j <= n; j++) {
f[i][j] = g[j - 1] + a[p[j]];
if (i > 1) f[i][j] += b[p[j]];
}
}
int ret = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (f[i][j] <= m) ret = i;
}
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
再给出贪心的做法。由于数对 已经按照 从小到大排序,当选择的方案中第一个数对是 ,最后一个数对是 ,那么 部分的代价是固定的 。现在要在 中满足 的条件下选择尽可能多的数对。显然贪心的做法是按照 从小到大去选,直到不满足条件。
所以具体做法是暴力枚举 和 ,在固定 枚举 时开一个 std::priority_queue
存储 中的 ,当 不满足时把最大的元素从中删除。最后如果满足条件此时优先队列的大小就是最多能选择的数对,将其与答案取最大值。
然后有疑问的地方是,如果此时优先队列中不同时存在 和 ,那么这么做是否还是正确的?此时优先队列中的 都来自 中的数对,意味着此时的代价 一定不小于 ,因此对答案没有影响。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2010;
int a[N], b[N], p[N];
void solve() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d %d", a + i, b + i);
p[i] = i;
}
sort(p + 1, p + n + 1, [&](int i, int j) {
return b[i] < b[j];
});
int ret = 0;
for (int i = 1; i <= n; i++) {
LL s = 0;
priority_queue<int> pq;
for (int j = i; j <= n; j++) {
s += a[p[j]];
pq.push(a[p[j]]);
while (!pq.empty() && s + b[p[j]] - b[p[i]] > m) {
s -= pq.top();
pq.pop();
}
if (s + b[p[j]] - b[p[i]] <= m) ret = max(ret, int(pq.size()));
}
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round #932 (Div. 2) Editorial:https://codeforces.com/blog/entry/126662
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18056807
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-03-06 正则问题
2022-03-06 树的DFS