给定一串1、0数字,可以选择任意区间翻转,0会变成1,1会变成0。问区间的取值种类有多少。
思维:
这个题也是匹配问题,因为 0 1 是相消的, 怎么体现相消呢,一个置1,一个置-1呗。再继续思考,对于每个区间如果数 > 0 意味着1多,翻过来总体的值就变小,< 0 意味着0多,反过来总体的值就变大。找区间的最大最小值。
再数学化一点:
假设 a 是一个原串的 1 的数量,b 是选择的区间的 0 的数量,c 是选择的区间的 1 的数量,则翻转后串的价值是 a+b-c
a 的数量是确定的,变的是 b-c 的数量,所以我们只需要知道 b-c 的值可能有多少种即可。所以考虑将字符 1 的位置看做数值 1,将字符 0 的位置看做数值-1这样使得区间数字和就是 b-c。
现在问题变成给定一个数组,问区间和存在多少种不同的值
由于数值只有 1 和-1,所以我们可以求最大区间子段和与最小区间
子段和的差值即可
码风一:能加就加原则,不能加就赋值
for (int i = 0; i < n; i ++ ) {
if(sum < 0) sum = a[i];
else sum += a[i];
mx = max(mx, sum);
}
码风二:tourist的思维,R(答案上界,一定是由某一个pref减去他前面的最小值),L(答案下界), mx(维护的是当前i之前的所有的最小值),mi。 注意,这套“模板”mx和mi的更新是在r和l之后的。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int main(){
//std::ios::sync_with_stdio(false);
//std::cin.tie(nullptr);
int n;
cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i ++ ) {
cin >> a[i];
a[i] = (a[i] == 1 ? 1 : -1);
}
vector<int> pref(n + 1, 0);
for (int i = 0; i < n; i ++ ) {
pref[i + 1] = pref[i] + a[i];
}
int L = 0;
int R = 0;
int mi = 0;
int mx = 0;
for (int i = 1; i <= n; i ++ )
{
R = max(R, pref[i] - mi);
L = min(L, pref[i] - mx);
mx = max(mx, pref[i]);
mi = min(mi, pref[i]);
}
cout << R - L + 1 << '\n';
return 0;
}
再数学化一点:
假设 a 是一个原串的 1 的数量,b 是选择的区间的 0 的数量,c 是选择的区间的 1 的数量,则翻转后串的价值是 a+b-c
a 的数量是确定的,变的是 b-c 的数量,所以我们只需要知道 b-c 的值可能有多少种即可。所以考虑将字符 1 的位置看做数值 1,将字符 0 的位置看做数值-1这样使得区间数字和就是 b-c。
现在问题变成给定一个数组,问区间和存在多少种不同的值
由于数值只有 1 和-1,所以我们可以求最大区间子段和与最小区间
子段和的差值即可
再数学化一点:
假设 a 是一个原串的 1 的数量,b 是选择的区间的 0 的数量,c 是选择的区间的 1 的数量,则翻转后串的价值是 a+b-c
a 的数量是确定的,变的是 b-c 的数量,所以我们只需要知道 b-c 的值可能有多少种即可。所以考虑将字符 1 的位置看做数值 1,将字符 0 的位置看做数值-1这样使得区间数字和就是 b-c。
现在问题变成给定一个数组,问区间和存在多少种不同的值
由于数值只有 1 和-1,所以我们可以求最大区间子段和与最小区间
子段和的差值即可