Codeforces - 1106E - Lunar New Year and Red Envelopes 优先队列+dp

题目链接

题意:现在有 k k k 个红包,总共 n n n 的时间, B o b Bob Bob 采用贪心策略,每个时间点若有红包能取则取钱数 w i w_i wi 最多的,且取完之后直到 d i d_i di 个时间点之后才能再取红包, A l i c e Alice Alice m m m 次机会在一个时间点让 B o b Bob Bob 不能做任何操作, A l i c e Alice Alice 怎么分配这 m m m 次机会才能使得 B o b Bob Bob 获得的钱数最少,问最少的钱数为多少。

思路:很明显是 d p dp dp,状态为第 i i i 个时间点, A l i c e Alice Alice 用了 j j j 次机会。对于每个点 B o b Bob Bob 要选择的红包,可以通过优先队列每次更新。由于 t i ≤ d i t_i \leq d_i tidi 所以对于一个时间点若选取第 i i i 个红包,则影响的状态的时间点优先队列里已经不存在第 i i i 个红包,所以直接 d p dp dp 即可。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <string>
#include <cmath>
using namespace std;
#define ll long long
#define PI acos(-1)
#define INF 0x3f3f3f3f
#define NUM 100005
#define debug true
#define lowbit(x) ((-x)&x)
#define ffor(i,d,u) for(int i=(d);i<=(u);++i)
#define _ffor(i,u,d) for(int i=(u);i>=(d);--i)
#define mst(array,Num,Kind,Count) memset(array,Num,sizeof(Kind)*(Count))
int n, m, k;
struct node
{
    int s, t, d;
    long long w;
    bool operator<(const node &x)const
    {
        if (w != x.w)
            return w < x.w;
        return d < x.d;
    }
}r[NUM];
long long dp[NUM][205];
priority_queue<node, vector<node>, less<node> > p;
template <typename T>
inline void read(T &x){
    char ch = getchar();x = 0;
    for (; ch < '0' || ch > '9'; ch = getchar());
    for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}
template <typename T>
inline void write(T x)
{
    int len=0;char c[21];
    if(x<0)putchar('-'),x*=(-1);
    do{++len;c[len]=(x%10)+'0';}while(x/=10);
    _ffor(i,len,1)putchar(c[i]);
}
inline bool cmp(const node &x, const node &y)
{
    return x.s < y.s;
}
inline void AC()
{
    int h = 1;
    node x;
    dp[0][0] = 0;
    read(n), read(m), read(k);
    ffor(i, 1, k) read(r[i].s), read(r[i].t), read(r[i].d), read(r[i].w);
    ffor(i, 1, n)
        ffor(j, 0, min(i, m))
            dp[i][j] = 100000000000005;
    sort(r + 1, r + 1 + k, cmp);
    ffor(i, 0, n)
    {
        while (h <= k && r[h].s <= i + 1)//压入可以选择的红包
        {
            p.push(r[h]);
            ++h;
        }
        if (!p.empty())//弹出已经超过时间点的红包
        {
            x = p.top();
            while (x.t < i + 1)
            {
                p.pop();
                if (p.empty())
                    break;
                x = p.top();
            }
        }
        ffor(j, 0, min(i, m))
            if(p.empty())//当前时间点没有红包可以选择
                dp[i + 1][j] = min(dp[i + 1][j], dp[i][j]);
            else
            {
                dp[x.d][j] = min(dp[i][j] + x.w, dp[x.d][j]);//Bob选择红包
                dp[i + 1][j + 1] = min(dp[i + 1][j + 1], dp[i][j]);//Alice使用一次机会
            }
    }
    long long ans = dp[n][0];
    ffor(i, 1, min(n, m)) ans = min(ans, dp[n][i]);
    write(ans);
}
int main()
{
    AC();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值