一篇题解
Tokai__Teio · · 题解
[AGC045B] 01 Unbalanced
先推一手广告,更好的阅读体验。
题意:
给定一个字符串,其中
分析:
一般对于最小化最大值的题,往往都可以用二分去做,(但是这道题的二分的 check 函数我不会写),那么就想一想有没有什么平替,我们可以想到贪心和 dp 也是可以解决类似的问题的,我们可以考虑因为是跟个数有关且只有两个不同的数,那么我们就可以将
然后我们需要去求
那么这样贪心为什么是正确的呢?
当
那么正确性保证了,最后的答案也就是
代码实现起来也是比较容易的,一些细节也在代码中标注了。
#include <cstring>
#include <iostream>
#include <algorithm>
namespace read_write //快读快写可以忽略
{
template <typename T>
inline void read(T &x)
{
x = 0;
T f = 1;
char ch = getchar();
while (ch > '9' || ch < '0')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = x * 10 + ch - '0', ch = getchar();
x *= f;
return;
}
template <typename T>
inline void write(T x)
{
if (x < 0)
{
x = -x;
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
T max(T x, T y)
{
return x > y ? x : y;
}
template <typename T>
T min(T x, T y)
{
return x > y ? y : x;
}
template <typename T>
void swap(T &a, T &b)
{
T tem = b;
b = a;
a = tem;
}
}
namespace Solve
{
using namespace read_write;
const int N = 1e6 + 10;
std::string a;
int sum[N]; //记录后缀中1的个数(与0抵消之后的个数)
int len;
int calc(int limit)
{
int cnt, mn;
cnt = mn = 0;
for(int i = 1 ; i <= len ; ++ i)
{
if(a[i] == '0')
cnt --;
else if(a[i] == '1')
cnt ++ ;
else
{
if(cnt + sum[i + 1] + 1 <= limit)
cnt ++;
else
cnt --;
}
mn = min(mn, cnt); //计算f[g]
}
return limit - mn;
}
void solve()
{
std::cin >> a;
len = a.size();
a = ' ' + a; //在字符串前加个空格,调整下标(个人习惯
for(int i = len ; i >= 1 ; -- i)
sum[i] = max(0, sum[i + 1] + (a[i] == '1' ? 1 : -1)); //在计算1的个数时也将 '?' 看成是0
write(min(calc(sum[1]), calc(sum[1] + 1))); //分别计算0多和1多的情况
}
}
int main()
{
Solve::solve();
return 0;
}