USACO 2023年12月铜组 COWNTACT TRACING 2(模拟 思维)

第二题:COWNTACT TRACING 2

标签:思维、枚举、模拟
题意:给定长度为 n n n的字符串(只包含 01 01 01),求用初始最少的 1 1 1每天每一个 1 1 1都会把相邻的两个位置都变成 1 1 1,经过若干天后形成给定的这个字符串。( 1 < = n < = 3 ∗ 1 0 5 1<=n<=3*10^5 1<=n<=3105
我们通过样例解释一下,比如要形成长度为 5 5 5 11111 11111 11111字符串。
可以一开始在第三个位置放 00100 00100 00100,第一天: 01110 01110 01110,第二天: 11111 11111 11111,所以初始只要一个 1 1 1
题解:比较容易想到我们需要把连续的 1 1 1的个数拆出来。比如 00011101101111001 00011101101111001 00011101101111001=> 3   2   4   1 3 \ 2 \ 4 \ 1 3 2 4 1
首先我们去思考每一团连续的 1 1 1要形成的最多天数是多少?(为什么是最多天数呢,因为天数越多,初始的 1 1 1就越少)不好想的话,那我们举几个例子:
1 1 1 0 0 0
11 11 11 0 0 0天(这里可能有些人会有疑惑,其实中间部分的和左右两边的情况 我们到时候要分类讨论的,这边姑且先考虑中间部分的)
111 111 111 1 1 1
1111 1111 1111 1 1 1
11111 11111 11111 2 2 2
111111 111111 111111 2 2 2
. . . . . . ...... ......
往下不断推下去,能推出最多天数其实是不是就是1的个数减去 1 1 1,再除以 2 2 2。(其实就是从最中间往外扩散的过程)

这是每一团连续的 1 1 1要形成的最多天数,那么我们要让每一团的符合,得去拿每一团的最多天数中的最小值( m i n D a y minDay minDay),让每一团连续的 1 1 1都满足天数要求。
因为每一团要让天数往下降,很简单,只要让初始的 1 1 1多一点就好了。
在求出 m i n D a y minDay minDay的基础上,我们接下来就是去求每一团在这样一个天数下,需要初始的 1 1 1的个数。

举个例子:比如 m i n D a y = 2 minDay=2 minDay=2,某一团连续的 1 1 1 1111111 1111111 1111111,那么我们初始的 1 1 1应该是 0010100 0010100 0010100
这道题还有个需要注意的点是:最左( 1 1 1)和最右( n n n)连续的 1 1 1具有特殊性,最大天数能到达这一团的 1 1 1的个数减一。因为如果最左设置 1 1 1,往左扩散是会碰墙,过不去的;最右同理。
代码

#include <bits/stdc++.h>
using namespace std;

vector<int> v;

int main() {
    int n;
    string s;
    cin >> n >> s;

    int pre = 0, suf = n - 1, cnt = 0, minDay = n + 1;
    while (s[pre] == '1') pre++;
    while (suf > pre && s[suf] == '1') suf--;

    if (pre != 0) {
        v.push_back(pre);
        minDay = min(minDay, pre - 1);
    }
    if (suf != n - 1) {
        v.push_back(n - 1 - suf);
        minDay = min(minDay, n - 2 - suf);
    }

    for (int i = pre; i <= suf; i++) {
        if (s[i] == '1') cnt++;
        else if (s[i] == '0') {
            if (cnt > 0) {
                v.push_back(cnt);
                minDay = min(minDay, (cnt - 1) / 2);
            }
            cnt = 0;
        }
    }
    if (cnt > 0) {
        v.push_back(cnt);
        minDay = min(minDay, (cnt - 1) / 2);
    }

    int ans = 0;
    for (int i = 0; i < v.size(); i++) {
        int k = v[i] / (minDay * 2 + 1);
        if (v[i] % (minDay * 2 + 1)) k++;
        ans += k;
    }
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值