2020ICPC·小米 网络选拔赛第二场(A题2020)

传送门

题意

给出一个字符串 S S S( ∣ S ∣ < = 1 e 5 |S|<=1e5 S<=1e5),串中只有’0’,‘1’,'2’三种字符,计算不相交的子序列"2020"的最大数量是多少。

题解

观察"2020"的特点,是左右一样的结构,于是可以分解为两个一样的子问题,即判断能组成多少第一个"20"串和多少第二个"20"串,把它们组合组合就是答案。问题就在于如何判断要组成多少个第一个"20"字串呢?暴力枚举显然不行,这时候就要想到二分,想要用二分首先就要判断是否符合二分单调的性质;想想发现如果能组成 X X X个"2020"子序列,显然也能组成 X − 1 X-1 X1个"2020"子序列,反之如果不能组成 X X X个"2020"子序列,显然也就不能组合出 X + 1 X+1 X+1个子序列。

于是就符合二分性质,那么就二分第一个"20"字串的数量,也就是二分答案,具体步骤看看代码吧。

AC code

/*******************************
*   Coder : He Shuo.           *
*   Type : Original Work       *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 55;

int n;
char s[MAXN];

int check(int x)
{
    
    
    int a1 = x,a2 = 0,a3 = 0,a4 = 0;
    ///a1 表示第一个"20"字符中2还需要多少个,a2则是'0'还需要多少个
    ///a3 表示第二个"20"字符中2还需要多少个,a4则是'0'还需要多少个
    
    for(int i = 1;i <= n;i ++)
    {
    
    
        if(s[i] == '2')
        {
    
    
            if(a1)a1 --,a2 ++;///来了一个'2',优先把第一个"20"中的'2'达成数量要求;
            else if(a3)a3 --,a4 ++;///达成之后就填第二个"20"的'2';
        }
        else if(s[i] == '0')
        {
    
    
            if(a2)a2 --,a3 ++;///来了一个'0',也是优先把第一个"20"中的'0'达成数量要求;
            else if(a4)a4 --;///达成之后就填第二个"20"的'0';
        }
    }
    if(a1 == 0 && a2 == 0 && a3 == 0 && a4 == 0)return 1;///所有数量都达成之后说明当前x符合条件
    return 0;///否则就不行
}

int main()
{
    
    
	while(scanf("%d",&n) + 1)
    {
    
    
        scanf("%s",s + 1);
        int l = 0,r = n / 4;///答案边界为[0,n/4],很显然的吧。
        while(l < r)
        {
    
    
            int mid = l + r >> 1;
            if(check(mid))l = mid + 1;
            else r = mid;
        }
        if(check(l) == 0)l --;///看看当前l符不符合,不符合就答案减一(本蒟蒻二分就喜欢这么写,保险一点)
        printf("%d\n",l);
    }

}

猜你喜欢

转载自blog.csdn.net/qq_43814654/article/details/109407083