BZOJ3152[Ctsc2013]组合子逻辑——堆+贪心
题目链接:
题目大意:
一开始有一个括号包含[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); } }