[AGC045B] 01 Unbalanced 题解

[AGC045B] 01 Unbalanced

题意:

给定一个字符串,其中 \(?\) 可以替换成 \(0\)\(1\)。让最小化 \(0\)\(1\) 的数量差。

分析:

一般对于最小化最大值的题,往往都可以用二分去做,(但是这道题的二分的 check 函数我不会写),那么就想一想有没有什么平替,我们可以想到贪心和 dp 也是可以解决类似的问题的,我们可以考虑因为是跟个数有关且只有两个不同的数,那么我们就可以将 \(0\) 看成是 \(-1\)\(1\)\(+1\),然后就转换成了最小化最大的累积和与最小的累积和的差值。先可以设,在累计和最大值不超过 \(limit\) 时,\(f_{limit}\) 为最大累计和的最小值,\(g\) 为最小累计和的最大值,那么我们的答案就是求 \(\min \left\{limit - f_{limit}\right\}(g\leq limit)\)

然后我们需要去求 \(g\),对于求 \(g\),我们可以贪心的去求解,在一开始我们先将所以的问号都设置成 \(-1\),然后在检查一遍,如果累计和不超过我们的 \(limit\) 的话,就将 \(-1\) 变成 \(1\)

那么这样贪心为什么是正确的呢?

\(limit\) 增加 \(2\) 时,最多只有一个 \(?\) 发生变换,所以 \(f_{limit+2}\leq f_{limit}+2\),那么 \(limit-f_{limit+2}\leq (limit+2)-(f_{limit}+2)\),那么我们最终的答案为 \(\min \left\{limit-f_{limit} \right\} \leq \min \left\{ (limit+2)-f_{limit+2} \right\}\)

那么正确性保证了,最后的答案也就是 \(\min(g-f_{g},(g+1)-f_{g+1})\)

代码实现起来也是比较容易的,一些细节也在代码中标注了。

#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)));   //加1为了防止sum[1]为0时,出些奇怪的错误
    }
}

int main()
{
    Solve::solve();

    return 0;
}
posted @ 2023-07-19 11:11  Tokai__Teio  阅读(42)  评论(0)    收藏  举报