【DP】 Codeforces Round 536 (Div. 2) E. Lunar New Year and Red Envelopes

题目地址:https://codeforces.com/contest/1106/problem/E

Lunar New Year is approaching, and Bob is going to receive some red envelopes with countless money! But collecting money from red envelopes is a time-consuming process itself.
Let’s describe this problem in a mathematical way. Consider a timeline from time 1 to n. The i-th red envelope will be available from time si to ti, inclusive, and contain wi coins. If Bob chooses to collect the coins in the i-th red envelope, he can do it only in an integer point of time between si and ti, inclusive, and he can’t collect any more envelopes until time di (inclusive) after that. Here si≤ti≤di holds.
Bob is a greedy man, he collects coins greedily — whenever he can collect coins at some integer time x, he collects the available red envelope with the maximum number of coins. If there are multiple envelopes with the same maximum number of coins, Bob would choose the one whose parameter d is the largest. If there are still multiple choices, Bob will choose one from them randomly.
However, Alice — his daughter — doesn’t want her father to get too many coins. She could disturb Bob at no more than m integer time moments. If Alice decides to disturb Bob at time x, he could not do anything at time x and resumes his usual strategy at the time x+1 (inclusive), which may lead to missing some red envelopes.
Calculate the minimum number of coins Bob would get if Alice disturbs him optimally.

Description

在时间线为 1~n 上有 k 个信封和 m 个干扰机会,每个信封有领取时间段 [s, t]、时间点 d(领了个这个信封之后,在 d 点前无法领取其他信封)、以及价值 w,对于每个干扰机会,可以使其在任意一个时间点无法进行领取操作直到x+1。
问最优使用不超过m次干扰下,将领取的最小信封价值总和。每次领取的策略是取当前可领取信封中 w 最大的,w 相同时取 d 最大的。

Solution

dp[i][j] 表示前 i 个时间点中用了 j 次机会所能得到的最少金币。

由于Bob每次要优先取 wi 最大,其次是取 di 最大,所以利用优先队列,在当前 i 这个时间点,将所有满足条件的信封放入优先队列,然后再进行转移。

对于每个时间点 i,如果当前没有满足条件的信封即优先队列为空时,就要把当前时间点的状态保留至下一个时间点。

$for(int j = 0; j <= m; j++) dp[i+1][j] = Min(dp[i+1][j], dp[i][j]);$

如果有可用的信封,那么对于每次干扰机会 j,都有选择干扰和不干扰两种操作。

如果不干扰:$dp[di+1][j] = min(dp[di+1][j], dp[i][j] + wi);$

如果干扰:$dp[i+1][j+1] = min(dp[i+1][j+1], dp[i][j])$(注意此时 j 必须小与 m)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#pragma GCC diagnostic error "-std=c++11"
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define fi first
#define se second
#define mst(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9 + 7;
const int MaxN = 1e5 + 5;

struct node {
int s, t, d, w;
bool operator < (const node& q) const {
if(q.w == w) return q.d > d;
return q.w > w;
}
}a[MaxN];;

priority_queue<node> q;
LL dp[MaxN][205];

bool cmp(node x, node y) { return x.s < y.s; }

LL Min(LL x, LL y) {
if(y == -1) return x;
if(x == -1) return y;
return min(x, y);
}

int main()
{
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for(int i = 1; i <= k; i++) {
scanf("%d %d %d %d", &a[i].s, &a[i].t, &a[i].d, &a[i].w);
}
sort(a + 1, a + 1 + k, cmp);
memset(dp, -1, sizeof(dp));
dp[1][0] = 0;
int num = 1;
for(int i = 1; i <= n; i++) {
while(num <= k && a[num].s <= i) q.push(a[num++]); // 选取合法信封
while(!q.empty() && q.top().t < i) q.pop(); // 排除不合法信封
if(q.empty()) { // 如果当前时间点没有可选信封,那么当前状态就保留至下一个时间点
for(int j = 0; j <= m; j++) dp[i+1][j] = Min(dp[i+1][j], dp[i][j]);
}
else {
node now = q.top();
for(int j = 0; j <= m; j++) {
if(dp[i][j] == -1) continue;
dp[now.d+1][j] = Min(dp[now.d+1][j], dp[i][j]+now.w); // 不干扰此次操作
if(j < m) { // 干扰此次操作
dp[i+1][j+1] = Min(dp[i+1][j+1], dp[i][j]);
}
}
}
}
LL ans = 1e18;
for(int i = 0; i <= m; i++) ans = min(ans, dp[n+1][i]);
ans = max(0LL, ans); // 注意ans可能为负
printf("%lld\n", ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值