贪心选做
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;
}

浙公网安备 33010602011771号