贪心选做

P11361 [NOIP2024] 编辑字符串

一生之敌.

首先我们不关心交换后的 \(0/1\) 具体顺序,只关心有几个匹配上的位置. 容易想到把 \(t\) 中连续 \(1\) 划分成一段,这一段中字符 \(s_i\) 可以任意交换,而且能匹配的先匹配一定不劣,这意味着两个串可以独立来看.

但是接下来考虑另一个问题:如何尽可能简化代码?如果使用双指针+分讨代码随便 \(100+\) 行,这对于 T1 来说是难以接受的.

考虑以下简化:

对于预处理,

  • 考虑处理出每个位置所在块的编号,编号具体是什么不重要,只用保证两两不同.
  • 对于每个块统计 \(0/1\) 个数.

对于统计答案,

  • 考虑匹配上无论 \(1/0\) 的先后都不劣,于是只要能匹配就匹配,并且对应段相同 \(0/1\) 个数减去 \(1\),答案加 \(1\).
  • 考虑不能匹配的位置,说明两串对应段一个只剩 \(0\),一个只剩 \(1\). 为了防止段错位导致的分讨,此时对应段不同 \(0/1\) 个数仍然减去 \(1\),答案不变.

于是代码就呼之欲出:

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
int T, n, ans, ad[maxn], bd[maxn], a1[maxn], a0[maxn], b1[maxn], b0[maxn]; 
string a, b, c, d;

int main() {
    ios :: sync_with_stdio(false); cin.tie(0); cout.tie(0);

    cin >> T;
    while(T--) {
        cin >> n >> a >> b >> c >> d; ans = ad[0] = bd[0] = 0; (a[0] & 1) ? ++a1[0] : ++a0[0]; (b[0] & 1) ? ++b1[0] : ++b0[0];
        for(int i = 1; i < n; i++) ad[i] = ((c[i] & 1) && (c[i - 1] & 1)) ? ad[i - 1] : i, (a[i] & 1) ? ++a1[ad[i]] : ++a0[ad[i]];
        for(int i = 1; i < n; i++) bd[i] = ((d[i] & 1) && (d[i - 1] & 1)) ? bd[i - 1] : i, (b[i] & 1) ? ++b1[bd[i]] : ++b0[bd[i]];
        for(int i = 0; i < n; i++) {
        	if(a1[ad[i]] && b1[bd[i]]) ans++, a1[ad[i]]--, b1[bd[i]]--;
        	else if(a0[ad[i]] && b0[bd[i]]) ans++, a0[ad[i]]--, b0[bd[i]]--;
        	else if(a0[ad[i]]) a0[ad[i]]--, b1[bd[i]]--;
        	else a1[ad[i]]--, b0[bd[i]]--;;
		} cout << ans << endl;
    }

    return 0;
}
posted @ 2025-06-24 17:31  Ydoc770  阅读(22)  评论(0)    收藏  举报