洛谷題目傳送門算法
DP題怕是都要大大的腦洞。。。。。。數組
首先,時間那麼大沒用,直接離散化。spa
第一問還好。根據題意容易發現,當一堆活動的時間有大量重疊的時候,更好的辦法是把它們所有安排到一邊去。那麼咱們轉移的時候也確定是要一塊一塊地轉移啦。code
設\(tot_{l,r}\)爲徹底被包含在\(l-r\)時間內活動總數,直接\(O(n^3)\)暴力求就行了。隊列
設\(pre_{i,j}\)爲時間\(1-i\)內一邊選\(j\)個時,另外一邊能選的最大值。枚舉一塊轉移的話,咱們的方程應該寫成這樣:get
\[pre_ {i,j}=\max\limits_{k=1}^i\{pre_ {k,j}+tot_{k,i},pre _{k,j-tot _{k,i}}\}\]it
(顯然兩種狀況都要考慮)io
而後答案就是\(\max\limits_{j=1}^n\{\min(pre_{m,j},j)\}\)啦(\(m\)爲離散化後的時間總長,不會超過\(2n\))class
這個數組爲何要叫\(pre\)呢?這是個前綴DP值。爲了第二問,咱們還要作個後綴DP,\(suf_{i,j}\)表示時間\(i-m\)內一邊選\(j\)個時,另外一邊能選的最大值,跟\(pre\)幾乎同樣的轉移,也是\(O(n^3)\)的。im
對於第二問,咱們顯然能夠確定\(s_i-t_i\)以內的活動都被一邊選走了。至於\(s_i\)以前和\(t_i\)之後選了多少,咱們也只好枚舉。設\(f_{l,r}\)爲一邊強制選\(l-r\)之間全部活動時最優的最小值,假定這一邊在前面選了\(x\)個,在後面選了\(y\)個,另外一邊最多能選多少也就知道了,有方程
\[f_{l,r}=\max\limits_{x=1}^m\max\limits_{y=1}^m\{\min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y})\}\]
而後第\(i\)個的答案就是\(f_{s_i,t_i}\)麼?注意千萬別掉入這個誤區!\(pre\)和\(suf\)只是保證了局部最優,而沒有保證全局最優。要說人話的話,就是可能有一個活動跨過了\(s_i\),然而\(f_{s_i,t_i}\)並無統計到它,只有擴大強制選的區間使得可以包含它,才能統計到最優解。因而須要枚舉強制選區間了,\(ans_i=\max\limits_{l=1}^{s_i}\max\limits_{r=t_i}^m\{f_{l,r}\}\)
這樣的話,整個\(f\)都必需要算出來,上面的枚舉算法就變成\(O(n^4)\)了,跑不動。
點開標籤發現有單調隊列?!蒟蒻就往單調性上面想了想,因而就有了一個結論:設枚舉\(x\)時有一個使答案最優的\(y\),那麼當\(x\)增大時,若是\(y\)也增大那麼答案不會更優。觀察上面那個式子\(\min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y})\),那麼由於\(pre,suf\)都是遞減的,因此很顯然咱們不能讓\(x,y\)變大而\(pre,suf\)變小。
因而,實現的時候,只要把\(y\)從大往小掃了,並不須要什麼單調隊列來維護它。
#include<cstdio> #include<algorithm> #define RG register #define R RG int #define G c=getchar() #define Upd(A,L,R) {chkmx(A[i][j],A[k][j]+tot[L][R]); \ if(j>=tot[L][R])chkmx(A[i][j],A[k][j-tot[L][R]]);} #define Calc(y) min(x+tot[l][r]+y,pre[l][x]+suf[r][y]) using namespace std; const int N=209,M=409,INF=1e9; int s[N],t[N],b[M],tot[M][M],pre[M][N],suf[M][N],f[M][M]; inline int in(){ RG char G; while(c<'-')G; R x=c&15;G; while(c>'-')x=x*10+(c&15),G; return x; } inline int min(R x,R y){return x<y?x:y;} inline void chkmx(R&x,R y){if(x<y)x=y;} int main(){ R n=in(),m=0,i,j,k,l,r,x,y,p0,p1,ans; for(i=1;i<=n;++i){ b[++m]=s[i]=in(); b[++m]=t[i]=in()+s[i]; } sort(b+1,b+m+1);//離散化 m=unique(b+1,b+m+1)-b-1; for(i=1;i<=n;++i){ s[i]=lower_bound(b+1,b+m+1,s[i])-b; t[i]=lower_bound(b+1,b+m+1,t[i])-b; for(l=1;l<=s[i];++l)//tot暴力求 for(r=m;r>=t[i];--r)++tot[l][r]; } for(i=1;i<=m;++i)//注意初始化 for(j=1;j<=n;++j)pre[i][j]=suf[i][j]=-INF; for(i=1;i<=m;++i) for(j=0;j<=tot[1][i];++j) for(k=1;k<=i;++k)Upd(pre,k,i); for(i=m;i;--i)//轉移很類似,搞了個宏定義 for(j=0;j<=tot[i][m];++j) for(k=i;k<=m;++k)Upd(suf,i,k); for(l=1;l<=m;++l) for(r=l+1;r<=m;++r) for(y=n,x=0;x<=n;++x){ p0=Calc(y);//p0爲最優決策,p1爲當前決策 while(y&&p0<=(p1=Calc(y-1)))p0=p1,--y; chkmx(f[l][r],Calc(y)); } ans=0; for(j=1;j<=n;++j)chkmx(ans,min(pre[m][j],j)); printf("%d\n",ans); for(i=1;i<=n;++i){ ans=0; for(l=1;l<=s[i];++l) for(r=m;r>=t[i];--r)chkmx(ans,f[l][r]); printf("%d\n",ans); } return 0; }