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);
    }
}
posted @ 2018-10-15 12:11  The_Virtuoso  阅读(313)  评论(0编辑  收藏  举报