翻译(参考):
t组数据,每组一个字符串,可以从前往后删除一段字符串和从后往前删除一段,删除的代价为max(剩余字符串中0的个数记为num1,删除的字符串中1的个数记为num2),求代价的最小值。
分析:
我们考虑用前缀和f[i]记录0~i中1的个数。
记len为剩余字符串长度,n1为剩余字符串内1的个数;剩余的字符串范围 (i,i+len],则
num1=f[i]+(f[n]-f[i+len])=f[n]+f[i]-f[i+len];
num2=len-(f[i+len]-f[i])=len+f[i]-f[i+len];
cost=max(num1,num2)=max(f[n],len) - (f[i+len]-f[i]) = max(f[n],len) - n1;
len越大时在len<=f[n]范围内max(f[n],len)不影响,在len>f[n]范围内max(f[n],len)越大;n1在任何范围内都越大。在len<=f[n]内,cost递减;在len>f[n]内max(f[n],len)越大的速率要比n1增大的速率快,为什么呢?因为max(f[n],len)增加的值为增加的字符串的长度,而n1增加的值为增加的字符串中1的个数(因为增加的字符串有1也有0)。所以在len等于f[n]的时候花费是最小的。
cost-len函数图像大致如下图:
证明如下:
记剩余字符串为s1,删去的字符串为s2,我们要使s1中的0尽可能小,使s2中的1也尽可能小,
cost=max(f[n]-n1,len-n1),我们考虑len与f[n]的关系
前言:costa,costb,costc都为对应情况下的花费,n1a,n1b,n1c都为对应情况下剩余字符串中1的个数,len为剩余字符串长度。
a)剩余字符串s1长度len等于f[n]:
costa=num1a=num2a=f[n]-n1a;
b)剩余字符串s1长度len小于f[n]:
num1b=len-n1b;
num2b=f[n]-n1b;
costb=num2=f[n]-n1b
因为a剩余的字符串长度大于b剩余的字符串长度,所以n1a>=n1b
所以costb>=costa,a情况比b情况更优
c)剩余字符串s1长度len大于f[n]:
num1c=len-n1c;
num2c=f[n]-n1c;
costc=len-n1c;
len>f[n],n1c>n1a;
n1c-n1a<=len-f[n] ---> len-f[n]-n1c+n1a>=0
costc-costa=(len-n1c)-(f[n]-n1a)=len-f[n] -n1c+n1a >=0;
所以costc>=costa,a情况比c情况更优
综上,在a情况下最优。故枚举长度为f[n]的字符串求其代价,对所有的长度为f[n]的字符串的代价求最小值。
详见代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 50;
int f[N]; //记录1的个数的前缀和
int main()
{
int t;
cin >> t;
while (t--)
{
char s[N];
cin >> s;
int n;
for (n = 0; s[n] != '\0'; n++)
f[n + 1] = f[n] + int(s[n] == '1'); //前缀和
int ans = f[n]; //剩余字符串大小为0
for (int i = 0; i + f[n] <= n; i++)
ans = min(ans, f[i] + f[n] - f[i + f[n]]);
cout << ans << endl;
}
}
ending
呜呜呜,太菜了,面向标程的题解,可能说的不是很清楚,有什么错误之处,求各位dalao指正