BZOJ 2288 【POJ Challenge】生日禮物(貪心+優先隊列)
阿新 • • 發佈:2017-05-06
ace urn ons target challenge pri 最大 font return
同樣,選擇一個正數刪去的過程中,兩邊的負數肯定也必須是沒有操作過的,
那麽問題就轉化為,給你一些數,請你選擇其中k個不相鄰的數,使得其和最小,
等同於BZOJ 1150 [CTSC2007]數據備份Backup
【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2288
【題目大意】
給出一列數,求最多取m段連續的數字,使得總和最大
【題解】
首先我們對數據進行合並處理,連續的一段正數或者連續的一段負數處理成一個數字,
之後我們發現,如果正數的個數小於等於m,那麽直接輸出正數的總和即可,
如果大於m,我們有些正數不選,或者選擇一些負數把左右兩端的正數並起來。
這個負數的選擇過程相當於減去這個數的絕對值,
正數選擇拿出去的過程也相當於減去這個數的絕對值,
在選擇一個負數合並的過程中,兩邊的正數必須是沒有被操作過的,
同樣,選擇一個正數刪去的過程中,兩邊的負數肯定也必須是沒有操作過的,
那麽問題就轉化為,給你一些數,請你選擇其中k個不相鄰的數,使得其和最小,
等同於BZOJ 1150 [CTSC2007]數據備份Backup
【代碼】
#include <cstdio> #include <algorithm> #include <queue> using namespace std; typedef pair<int,int> P; const int N=100010; const int INF=0x3f3f3f3f; int n,m,a[N],b[N],l[N],r[N]; int main(){ while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;i++)scanf("%d",&a[i]); while(a[n]<=0)n--;int st=1; while(a[st]<=0)st++; int cnt=0,ans=0; for(;st<=n;st++){if(!((a[st]>0)^(a[st-1]>0)))b[cnt]+=a[st];else b[++cnt]=a[st];} for(int i=1;i<=cnt;i++)if(b[i]>0){ans+=b[i];m--;}else b[i]=-b[i]; if(m>=0){printf("%d\n",ans);continue;} priority_queue<P,vector<P>,greater<P> >Q; for(int i=1;i<=cnt;i++)l[i]=i-1,r[i]=i+1,Q.push(P(b[i],i)); r[cnt]=0; for(int i=1;i<=-m;i++){ while(b[Q.top().second]!=Q.top().first)Q.pop(); int x=Q.top().second;Q.pop(); ans-=b[x]; if(!l[x]){b[r[x]]=INF;l[r[x]]=0;} else if(!r[x]){b[l[x]]=INF;r[l[x]]=0;} else{ b[x]=b[l[x]]+b[r[x]]-b[x]; b[l[x]]=b[r[x]]=INF; r[l[x]=l[l[x]]]=l[r[x]=r[r[x]]]=x; Q.push(P(b[x],x)); } }printf("%d\n",ans); }return 0; }
BZOJ 2288 【POJ Challenge】生日禮物(貪心+優先隊列)