1. 程式人生 > >【BZOJ2436】NOI嘉年華(動態規劃)

【BZOJ2436】NOI嘉年華(動態規劃)

pri spa 單調性 return uniq () num 其中 print

【BZOJ2436】NOI嘉年華(動態規劃)

題面

BZOJ

題解

考慮第一問如何求解
發現狀態與選擇了哪些活動無關,只與時間有關
\(f[i][j]\)表示前\(i\)個單位時間(離散後),一個嘉年華選擇了\(j\)個活動時
另外一個可以選擇的最多的活動數量
轉移的話枚舉一下轉移過來的時間\(k\)
考慮時間\([k..i]\)的活動分配給哪個嘉年華就好了
所以\(f[i][j]=max(f[k][j]+sum[k][i],f[k][j-sum[k][i]])\)
其中\(sum[i][j]\)表示時間\([i,j]\)中能夠進行的所有活動的數量。
時間復雜度\(O(n^3)\)

考慮第二問

顯然是中間一段強制選,然後剩下的地方被拆成了兩段
然後考慮最大值。
註意,這強制選的一段不一定恰好是當前必須選的活動的這一段時間
而是可以向兩邊拓展
所以我們需要先預處理\(dp[i][j]\)表示時間\([i,j]\)的所有活動必須選擇的最優值
\(g[i][j]\)\(f\)表示相同的含義,但是時間不是\([1,i]\)而是\([i,n]\),即倒著選。
枚舉前面和後面分割出來的時間某個嘉年華分別選擇了幾個活動,
這樣很容易轉移,相當於是把中間強制選擇的那段時間的所有活動直接加給活動較少的那個嘉年華。
但是這樣的復雜度是\(O(n^4)\)的。

我們需要優化這個轉移,假設枚舉選擇了多少個活動的那個嘉年華

在強制選擇的區間的前面選了\(x\)個活動,後面選了\(y\)個活動
發現當\(x\)增大時,另外一個嘉年華能夠選擇的個數會減小,
而中間強制選擇的區間的值不會變化,如果繼續增加\(y\)的話,發現另外一個嘉年華能夠選擇的值也會更小,所以\(x\)增加時,\(y\)減少。
這樣子可以利用單調性優化掉一個\(n\),時間復雜度變為\(O(n^3)\)

每次回答詢問的時候,確定當前強制選擇的活動左右端點,暴力向左右拓展就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 444 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int L[MAX],R[MAX],S[MAX],tot,num[MAX][MAX]; int f[MAX][MAX],g[MAX][MAX],d[MAX][MAX],ans,n; void cmax(int &x,int y){if(x<y)x=y;} int Calc(int i,int j,int x,int y) { if(f[i][x]<0||g[j][y]<0)return -1e9; int t=f[i][x]+g[j][y]; return min(max(t,x+y),min(t,x+y)+num[i][j]); } int main() { n=read(); for(int i=1;i<=n;++i)L[i]=read(),R[i]=read()+L[i]; for(int i=1;i<=n;++i)S[++tot]=L[i],S[++tot]=R[i]; sort(&S[1],&S[tot+1]);tot=unique(&S[1],&S[tot+1])-S-1; for(int i=1;i<=n;++i)L[i]=lower_bound(&S[1],&S[tot+1],L[i])-S; for(int i=1;i<=n;++i)R[i]=lower_bound(&S[1],&S[tot+1],R[i])-S; for(int i=0;i<=tot+1;++i) for(int j=i;j<=tot+1;++j) for(int k=1;k<=n;++k) if(i<=L[k]&&R[k]<=j)++num[i][j]; memset(f,-63,sizeof(f));f[0][0]=0; for(int i=1;i<=tot;++i) for(int j=0;j<=n;++j) for(int k=0;k<i;++k) { cmax(f[i][j],f[k][j]+num[k][i]); if(j>=num[k][j])cmax(f[i][j],f[k][j-num[k][i]]); } for(int i=1;i<=tot;++i)ans=max(ans,min(i,f[tot][i])); printf("%d\n",ans); memset(g,-63,sizeof(g));g[tot+1][0]=0; for(int i=tot;i;--i) for(int j=0;j<=n;++j) for(int k=i+1;k<=tot+1;++k) { cmax(g[i][j],g[k][j]+num[i][k]); if(j>=num[k][j])cmax(g[i][j],g[k][j-num[i][k]]); } for(int i=1;i<=tot;++i) for(int j=i;j<=tot;++j) if(num[i][j]) { int y=n; for(int x=0;x<=n;++x) { int nw=Calc(i,j,x,y); while(y) { int nt=Calc(i,j,x,y-1); if(nw<=nt)--y,nw=nt; else break; } d[i][j]=max(d[i][j],nw); } } for(int i=1;i<=n;++i) { ans=0; for(int j=1;j<=L[i];++j) for(int k=R[i];k<=tot;++k) ans=max(ans,d[j][k]); printf("%d\n",ans); } return 0; }

【BZOJ2436】NOI嘉年華(動態規劃)