上海计算机学会2023年12月月赛C++丙组T5特定的串¶
keywords: 贪心
题目链接¶
题目描述¶
给定一个01序列,要求序列不能存在110的组合,问最少操作次数是多少
思路¶
看数据范围 5e5,这应该是一个贪心,否则,就搞不成了 具体怎么贪,就是一个技术活
方案1¶
// 这种贪心,只能30pts
#include <bits/stdc++.h>
using namespace std;
string s;
int cnt, n;
int main() {
    cin >> n;
    cin >> s;
    for (int i = n - 1; i >= 2; i--) {
        if (s[i] == '0' && s[i - 1] == '1' && s[i - 2] == '1') {
            cnt++;
            s[i - 2] = 0;
        }
    }
    cout << cnt << '\n';
    return 0;
}
方案2¶
// 这样贪心,可以对6个点
#include <bits/stdc++.h>
using namespace std;
string s;
int cnt, n;
int main() {
    cin >> n;
    cin >> s;
    for (int i = 2; i < n; i++) {
        if (s[i] == '0' && s[i - 1] == '1' && s[i - 2] == '1') {
            if (i == 2 || i == 3) {
                s[i - 2] = 0, cnt++;
                continue;
            }
            if (!(s[i - 3] == '1' && s[i - 4] == '1')) {
                s[i - 2] = '0';
                cnt++;
                continue;
            }
            s[i] = '1';
            cnt++;
        }       
    }
    cout << cnt << '\n';
    return 0;
}
方案3¶
#include <bits/stdc++.h>
using namespace std;
string s;
int ret, n, zero;
vector<int> A;
int main() {
    cin >> n;
    cin >> s;
    bool flag = false;
    for (int i = 0; i < n; i++) {
        if (!flag && s[i] == '0') continue; // 开头是0,就跳过
        flag = true;
        int j = i, t = 0;
        while (j < n && s[j] == s[i]) t++, j++;
        A.push_back(t);
        i = j - 1;
    }
    int len = A.size();
    if (len % 2) A.pop_back(); // 保证是长度是偶数,就是一段1一段0的101010交替组合
    for (int i = 1, len = A.size(); i < len; i += 2)
        zero += A[i];
    for (int i = 0, len = A.size(); i < len; i += 2){
        if (A[i] >= 2 && i + 1 < len && A[i + 1] >= 1) {
            ret += min(A[i] / 2, zero); // {把这段111111改成1010101,把后面的0全部变成1}
        }
        zero -= A[i + 1]; // 右边还剩下多少个0
    }
    cout << ret << '\n';
    return 0;
}
总结¶
比赛场上,想不到完整思路,就要实现部分思路。不能不写,不能放弃,不能不得分。
AC代码的思路,来源于,贾宸浩同学
,我完善了一下代码