传送门


这里只说的把,也就是暴力枚举删除区间

似乎有更快的算法,以后再补


当删除某段区间后,最终剩下一段前缀和一段后缀作括号匹配

会自动消除

对于一段序列来说消除的顺序是不影响最终的结果的

不妨先对前缀做括号匹配,变成的形式

对后缀也做括号匹配,变成的形式

不难发现,此时能消除的只剩下前缀后面的连续零和后缀前面的连续一

所以直接预处理这两部分,还要预处理前缀自己匹配剩下的长度和后缀剩下的长度

用栈模拟括号匹配即可

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,top,pre[maxn],pre0[maxn],suf[maxn],suf1[maxn];
int o[maxn],z[maxn];
char a[maxn],q[maxn];
int main()
{
    cin >> n >> (a+1);
    int zero = 0;
    for(int i=1;i<=n;i++)
    {
        o[i] = o[i-1]+(a[i]=='1');
        z[i] = z[i-1]+(a[i]=='0');
        if( !top )    q[++top] = a[i],zero = ( a[i]=='0' );//栈空,没问题 
        else    
        {
            if( q[top]=='0'&&a[i]=='1' )    top--,zero--;
            else//不能消除,栈顶是0自己也是0,栈顶是1自己是0或1 
                q[++top] = a[i], zero += ( a[i]=='0' );
        }
        pre[i] = top, pre0[i] = zero;
    }
    int ans = top;
    int one = 0; top = 0;
    for(int i=n;i>=1;i--)
    {
        if( !top )    q[++top] = a[i], one = ( a[i]=='1' );
        else
        {
            if( q[top]=='1'&&a[i]=='0' )    top--,one--;
            else
                q[++top] = a[i], one += ( a[i]=='1' );
        }
        suf[i] = top, suf1[i] = one;
    }
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    {
        if( o[j]-o[i-1]!=2*(z[j]-z[i-1]) )    continue;
        ans = min( ans,pre[i-1]+suf[j+1]-min( pre0[i-1],suf1[j+1])*2 ); 
    }
    cout << ans;
    return 0;
}