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<bits/stdc++.h>
#define dd(x) cout<<#x<<" = "<<x<<" "
#define de(x) cout<<#x<<" = "<<x<<"
"
#define sz(x) int(x.size())
#define All(x) x.begin(),x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
typedef priority_queue<P> BQ;
typedef priority_queue<int,vector<int>,greater<int> > SQ;
const int maxn=1e5+10,mod=1e9+7,INF=0x3f3f3f3f;
vector<pair<P,int> > S[maxn],T[maxn];
multiset<pair<P,int> > 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;
}