题目链接在本地,题目大意是从一段只包含0,1,2 的字符串中选出若干个子序列“2020”,选出一个2020序列以后,这四个数字从原来的位置上删除,问最多能选出多少个这种序列。
一开始想的贪心思想是从左往右扫描,越靠左满足2020的越先选出来,后来发现了反例就是20202200,如果越靠左满足的越先选出来,那这个只能选出一个2020,但是我们可以先选左边的20,然后在后四个中取出一个20,另一个同理,因此这种贪心思想是错误的。
我们现在考虑另一种贪心思想,对于出现的2和0,我们一定把它当做第一个2和0,为什么这么做可以参考上面的反例。但是这个新的20不能全当新的,一定是到了一个数目为止,后面出现的20都作为第二个20,现在我们需要知道这个数是多少,这个步骤用二分做即可。
这题主要难想的是贪心的思想,一定要先满足前面的够用才能统计后面的20,不然就会出现反例中的情况。
1 #include "bits/stdc++.h" 2 using namespace std; 3 const int MAX=1e5+5; 4 int n,a[5]; 5 char s[MAX]; 6 bool feasible(int x){ 7 int i,j; 8 a[1]=a[2]=a[3]=a[4]=0; 9 for (i=1;i<=n;i++){ 10 if (s[i]=='2'){ 11 if (a[1]!=x) a[1]++; 12 else if (a[3]<a[2]) a[3]++; 13 } 14 if (s[i]=='0'){ 15 if (a[2]!=x){ 16 if (a[2]<a[1]) a[2]++; 17 } 18 else{ 19 if (a[4]<a[3]) a[4]++; 20 } 21 } 22 if (a[4]==x) return true; 23 } 24 return false; 25 } 26 27 int main(){ 28 // freopen ("b.in","r",stdin); 29 // freopen ("b.out","w",stdout); 30 int i,j,low,high,mid,ans; 31 while (scanf("%d",&n)!=EOF){ 32 scanf("\n%s",s+1); 33 low=1,high=n; 34 ans=0; 35 while (low<=high){ 36 mid=(low+high)>>1; 37 if (feasible(mid)){ 38 ans=mid; 39 low=mid+1; 40 } 41 else high=mid-1; 42 } 43 printf("%d\n",ans); 44 } 45 return 0; 46 }