1. 程式人生 > 實用技巧 >CF1106E Lunar New Year and Red Envelopes DP

CF1106E Lunar New Year and Red Envelopes DP

原題連結:Lunar New Year and Red Envelopes

題目大意

總時長為\(n\),一共有\(k\)個紅包,第\(i\)個紅包會在\([s_i,t_i]\)時間段中出現,每個紅包有一個價值\(w_i\)以及一個冷卻值\(d_i\)表示如果選取了這個紅包,則直到\(d_i\)時刻都不能再取紅包(包含\(d_i\)).在任意一個時刻\(i\)時,如果不在冷卻時間內,則這個人會選取所有紅包中價值最高的紅包,如果有多個則選取\(d\)最大的紅包,如果還有重複的就任選一個.

另外一個人可以執行\(m\)次干擾,使得他不能在某個時刻領取任何紅包,問在執行最優策略下,對方能拿到最少多少錢的紅包.

資料範圍:

\(1 \leq n,k \leq 10^5\)

\(1 \leq m \leq 200\)

\(1 \leq s_i\leq t_i \leq d_i \leq n\)

思路

還是比較套路的思考方式,首先考慮沒有干擾的前提下,這個人會怎麼選取紅包.由於每個時刻的選法都是固定的,可以確定一個條件:這個人在每個時刻選取的紅包的策略是固定的,這個事情可以預處理出來,記\(s_i={w_i,d_i}\)表示在\(i\)時刻選取的紅包的價值是\(w_i\),冷卻時間是\(d_i\).暫時不考慮如果本時刻沒有紅包怎麼設定.

假設當前已經把預處理的\(s_i\)做出來了,那麼進一步的考慮怎麼把干擾條件加進去,可以發現干擾策略只關注當前的時刻以及當前執行了多少次干擾,可以直接dp:

  • 狀態:\(f[i][j]\)表示當前時刻是\(i\)且已經執行了\(j\)次干擾時,對方能拿到的所有價值集合中的最小价值是多少
  • 入口:\(f[1][0]=0\)其他全為\(+\infin\).
  • 轉移:考慮當前\(i\)時刻是由哪個時刻轉移而來的比較困難,考慮從出邊轉移:如果當前時刻執行了干擾,那麼可以直接走到下一個時刻而不產生任何價值的貢獻,也即\(f[i + 1][j + 1] = f[i][j]\),反之如果在此時刻沒有執行干擾,則對方一定會在此刻選擇一個紅包,也就是\(f[s[i].d + 1][j]=f[i][j] +s[i].w\).由於冷卻時間是包含\(d_i\)的所以要轉移到\(d_i+1\)
    .
  • 出口:由於是出邊轉移,答案最終不會儲存在\(f[n][j]\)上而是\(f[n+1][j]\)上,取\(res=\min_{j\in[0,m]}{f[n+1][j]}\)即可.

由於某個時刻可能沒有紅包,所以還有一種額外的情形就是當前雖然沒有執行干擾但是價值也沒有貢獻,這種情況可以直接把\(s[i]\)設定成\(d=i,w=0\)代入表示式意義就是轉移到下一個時刻但是沒有價值.

考慮預處理部分,由於需要對\({w,d}\)這樣的雙關鍵字排序,並且需要動態的指定插入和刪除,所以丟map維護,維護當前\({w,d}\)的二元組的數量,如果數量達到\(0\)就刪除,查詢時查詢兩者皆最大者.那麼求出所有紅包覆蓋的時間段這個操作可以用一個事件來維護:\({t,w,d,type}\)表示\(t\)時刻有一個價值是\(w\)冷卻時間是\(d\),型別是\(type\)的時間,如果是增加的事件就往map裡面插入,反之就刪除.如果當前map裡面沒有元素就表示這個時刻沒有紅包可以取反之取最大即可.

那麼最後預處理給所有事件按事件排個序維護一下即可.

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
typedef pair<int,int> pii;
#define x first
#define y second

const int N = 1e5+7,M = 205;
struct Event
{
	int t,d,w;
	int type;
	bool operator<(const Event& _o)	const
	{
		if(t != _o.t)	return t < _o.t;
		if(w != _o.w)	return w > _o.w;
		return d > _o.d;
	}
};
pii s[N];
ll f[N][M];
int main()
{
	int n,m,k;scanf("%d%d%d",&n,&m,&k);
    vector<Event> E;
    forn(i,1,k)
    {
    	int l,r,d,w;scanf("%d%d%d%d",&l,&r,&d,&w);
    	E.push_back({l,d,w,1});
    	E.push_back({r + 1,d,w,-1});
    }
    sort(E.begin(),E.end());
    
    memset(s,-1,sizeof s);
    map<pii,int> st;
    
    int j = 0;
    forn(i,1,n)
    {
    	while(j < E.size() && E[j].t <= i)
    	{
    		auto e = E[j];
    		if(e.type == 1)	st[{e.w,e.d}] ++;
    		else
    		{
    			if(--st[{e.w,e.d}] == 0)	st.erase({e.w,e.d});
    		}
    		++j;
    	}
    	
    	if(st.empty())	s[i] = {0,i};
    	else s[i] = {(*st.rbegin()).x.x,(*st.rbegin()).x.y};
    }
    
    memset(f,0x3f,sizeof f);
    f[1][0] = 0;
    forn(j,0,m)	forn(i,1,n)
    {
    	f[i + 1][j + 1] = min(f[i + 1][j + 1],f[i][j]);
    	f[s[i].y + 1][j] = min(f[s[i].y + 1][j],f[i][j] + s[i].x);	
    }
	
	ll res = 1e18;
	forn(i,0,m)	res = min(res,f[n + 1][i]);
	printf("%lld",res);
    
    return 0;
}