CF1809D Binary String Sorting 题解
贪心。由于每次操作的代价都很大,所以需要优先减少操作次数,然后尽量多使用交换操作。
易得交换操作最多只会发生一次,因为每次交换操作只能消除一个逆序对,当存在两个或多个逆序对时,可以通过删除操作来减少更多的逆序对,减少操作次数。当只存在一个逆序对时,自然也只能交换一次。
然后,由于排序结束后 \(0\) 和 \(1\) 之间必然有一条分界线,左边是 \(0\),右边是 \(1\)。我们考虑枚举这条分界线,在分界线左边的 \(1\) 需要删除,在分界线右边的 \(0\) 需要删除。在分界线的同侧进行交换操作是无意义的,因为这并不会减少左边的 \(1\) 或右边的 \(0\)。
如果一次交换操作能恰好把左边的 \(1\) 换到右边,右边的 \(0\) 换到左边,那么可以优先执行这个交换操作,我们发现这种情况只会在分界线左右第一个元素发生,正好印证了上文交换操作最多只会发生一次。所以,当分界线左边第一个元素为 \(1\),右边第 \(1\) 个元素为 \(0\) 时,需要进行一次交换操作,从而减少两次修改操作。
如果分界线不满足左边第一个元素为 \(1\),右边第 \(1\) 个元素为 \(0\) 时,其实也不用处理分界线左右第一个元素。因为无论是 \(00\) 还是 \(11\) 还是 \(01\),其实都是符合条件的数对,并不会影响结果。
#include <bits/stdc++.h>
using namespace std;
long long t,n,sf=1e12,df=1e12+1;
char str[400000];
int main()
{
scanf("%lld",&t);
while(t--)
{
long long s0=0,s1=0,z=0,y=0,ans=1e18;
scanf("%s",str+1);
n=strlen(str+1);
for(int i=2;i<=n;i++)
if(str[i]=='0')y++;
for(int i=1;i<=n-1;i++)
{
if(str[i+1]=='0')y--;
if(str[i-1]=='1')z++;
if(str[i]=='1'&&str[i+1]=='0')ans=min(ans,(z+y)*df+sf);
else ans=min(ans,(z+y)*df);
}
if(ans==1e18)printf("0\n");
else printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号