1. 程式人生 > >BZOJ3152[Ctsc2013]組合子邏輯——堆+貪心

BZOJ3152[Ctsc2013]組合子邏輯——堆+貪心

題目連結:

BZOJ3152

 

題目大意:

一開始有一個括號包含[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);
    }
}