Codeforces 1106E. Lunar New Year and Red Envelopes(DP)

E. Lunar New Year and Red Envelopes

题意:
在长度为n的时间轴上,有k个红包,每个红包有领取时间段[s,t],价值w,以及领了个这个红包之后,在时间d到来之前无法再进行领取操作。每次领取的策略是取当前可领取红包中w最大的,w相同时取d最大的。再给m个干扰机会,一个干扰机会可以使其在任意一个x这个时间点无法进行领取操作直到x+1。问最优使用不超过m次干扰下,将领取的最小红包价值总和。(n,k<=1e5,m<=200)
思路:
这场因为评测机出问题,UR了。前四题没什么价值,这题DP的感觉出的挺好。
虽然状态的定义和转移没什么难度(dp[i][j]从i到n使用j次干扰的最小领取红包价值,这样定义方便倒着推,倒着推则是由于红包的d属性的存在),但是处理每个时间点应该领取哪个红包的实现方式挺巧妙的,用两个vector数组分别记录每个时间点有哪些红包出现和消失,再用set动态维护当前应领取的红包。
代码:

#include
#define dd(x) cout<<#x<<" = "< P;
typedef priority_queue

BQ; typedef priority_queue,greater > SQ; const int maxn=1e5+10,mod=1e9+7,INF=0x3f3f3f3f; vector > S[maxn],T[maxn]; multiset > st; ll dp[maxn][205]; int main() { int n,m,k; cin>>n>>m>>k; for (int i=1;i<=k;++i) { int s,t,d,w; scanf("%d%d%d%d",&s,&t,&d,&w); S[s-1].pb(mp(mp(w,d),i)),T[t].pb(mp(mp(w,d),i)); } memset(dp,INF,sizeof(dp)); dp[n+1][0]=0; for (int i=n;i;--i) { for (auto& j:T[i]) st.insert(j); for (auto& j:S[i]) st.erase(j); if (st.empty()) for (int j=0;j<=m;++j) dp[i][j]=dp[i+1][j]; else { int w=st.rbegin()->fi.fi,d=st.rbegin()->fi.se; for (int j=0;j<=m;++j) dp[i][j]=dp[d+1][j]+w; for (int j=1;j<=m;++j) dp[i][j]=min(dp[i][j],dp[i+1][j-1]); } } cout<<*min_element(dp[1],dp[1]+m+1); return 0; }

你可能感兴趣的:(Codeforces 1106E. Lunar New Year and Red Envelopes(DP))