BZOJ3152[Ctsc2013]組合子邏輯——堆+貪心
阿新 • • 發佈:2018-11-12
題目連結:
題目大意:
一開始有一個括號包含[1,n],你需要加一些括號,使得每個括號(包括一開始的)所包含的元素個數要<=這個括號左端點那個數的大小,當一個括號包含另一個括號時,裡面那個括號內所有數整體被看作是一個元素。
假設一個括號包含[L,R],它之中有一個括號包含[l,r],那麼這段區間長度最長為L+l-1,也就可以看做這段區間前L個被L括起來,後l-1個被l括起來。
那麼題目也就可以轉化成選擇一個數num可以覆蓋以他為左端點的往後num個數,詢問最少選幾個數能覆蓋整個數列。
剛開始第一個數一定要選的,那麼先往後覆蓋a1個數,要想再往後覆蓋就要在這前a1個數中再找一個數來繼續覆蓋下去。
因為要使得所選的數儘量少,所以要選前面中ai最大的。
那麼我們用堆維護這個東西,每當一個數ai被覆蓋時將它壓入堆中,噹噹前選的數覆蓋不了時再在堆頂選取最大的那個繼續覆蓋下去。
注意ai=1的特判。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> using namespace std; priority_queue<int>q; int T; int n; int a[2000010]; int now; int ans; int flag; int main() { scanf("%d",&T); while(T--) { while(!q.empty()) { q.pop(); } ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } if(n==1) { printf("0\n"); continue; } now=a[1]-1; ans++; flag=0; if(now==0) { printf("-1\n"); continue; } for(int i=2;i<=n;i++) { if(now==0) { now=q.top()-1; q.pop(); if(now==0) { flag=1; break; } ans++; } now--; q.push(a[i]); } if(flag==1) { printf("-1\n"); continue; } printf("%d\n",ans); } }