Codeforces Round #536 E. Lunar New Year and Red Envelopes /// 贪心 记忆化搜索 multiset取最大项

题目大意:

给定n m k;(1≤n≤1e5, 0≤m≤200, 1k1e5)

表示n个时间长度内 最多被打扰m次 k个红包

接下来k行描述红包 s t d w;(1std1w1e9

表示在 s 到 t 的时间内都可开始获得该红包

该红包在时间 d 时才能完成获得 红包内有w硬币

在同一时间段内只能获得一个红包 不能同时获得两个及以上

求在被打扰的情况下使获得的红包硬币最少 是多少

用in out标记各红包可被获得的区间 

用multiset维护在某个时间点可获得的红包有哪些 (放入in内的 去掉out内的)

multiset内部按w排序 那么在某个时间点取出w最大的就是获得红包的贪心策略 .rbegin()可获得multiset/set内的最后一项

按时间点记忆化搜索一下 dp[ 时间点 ][ 被打扰次数 ]来记忆

以上存储信息要用pair<> 用结构体会MLE

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define mem(i,j) memset(i,j,sizeof(i))
#define pb push_back
#define mp(i,j) make_pair(i,j)
#define fir first
#define sec second
#define P pair<LL,LL>
const int N=1e5+5;
P best[N]; // best[i] 在i位置的最佳策略
LL n, m, k;
LL dp[N][205];
// 记忆化 dp[i][j]在i位置被打扰了j次能获得的最少硬币
multiset <P> now;
// 维护当前位置所有能取的红包 pair默认按first排序
vector <P> in[N], out[N];
// in为从该位置开始可取的 out为从该位置开始不可取的

void init() {
    mem(dp,-1); now.clear();
    for(int i=1;i<=n+1;i++)
        in[i].clear(), out[i].clear();
}
LL DFS(int ind,int c) {
    if(ind>n) return 0LL;
    if(dp[ind][c]>=0LL) return dp[ind][c];
    LL res=best[ind].fir+DFS(best[ind].sec+1,c); // 取这个红包
    if(c<m) res=min(res,DFS(ind+1,c+1)); // 被女儿阻止
    return dp[ind][c]=res;
}

int main()
{
    while(~scanf("%d%d%d",&n,&m,&k)) {
        init();
        for(int i=0;i<k;i++) {
            LL l,r,d,w;
            scanf("%I64d%I64d%I64d%I64d",&l,&r,&d,&w);
            in[l].pb(mp(w,d));
            out[r+1].pb(mp(w,d));
        }
        for(int i=1;i<=n+1;i++) {
            for(int j=0;j<in[i].size();j++)
                now.insert(in[i][j]); // 加入可取的
            for(int j=0;j<out[i].size();j++)
                now.erase(now.find(out[i][j])); // 去掉不可取的
            if(now.size()) best[i]=*now.rbegin(); // 最佳策略是w最大的
            else best[i]={0LL,(LL)i};
        }
        printf("%I64d
",DFS(1,0));
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zquzjx/p/10345461.html