我讨厌思维题 /(ㄒoㄒ)/~~
原题链接:Problem - 1678B2 - Codeforces
题意翻译
每次给定一个长度为 n 的 01
串 s 。你可以对它做出 0 次或任意次操作。每次操作为选择 1 位并将其修改为 0
或 1,
使得 s 是好的。
一个字符串是好的,如果他存在一种字符串划分方式,分成 长度为偶数的不交字串,且一个字串中所有字符均相同 。如 s="11001111",它可以被分成 "11", "00" 和 "1111",他们的长度分别为 2,2,4(都是偶数),他是好的。另一个例子 s="1110011000",它可以被分成 "111", "00", "11" 和 "000",他们的长度分别为 3,2,2,3,不是好的。
你还需要输出将字符串变成 好的 的最小操作数,并输出在这种操作数下,使得你所划分出的字串数量最小,并输出最小字串数目。
输入格式
第一行包含一个正整数 t(1≤ t ≤ 10000)——表示测试用例的数量。
对于每个测试用例,第一行包含单个整数 n()——表示 s 的长度,保证n是偶数。
第二行包含长度为 n 的二进制字符串 s,该字符串仅由0和1组成。
第二行包含 n 个整数 ——表示点的点权。
保证所有测试用例的 n 之和不超过。
输出样例
思路
错误:
将相同的字符合并,可以得到一个数组 a[] ,a[i] 表示第 i 个串的长度,如 s="1110011000" 可以变成 a[]={0, 3, 2, 2, 3} ( a[i] 中根据 i 可以求出这个串里面放的是 0 还是 1,即 i%2=0/1,会方便一点)。
想让字符串变成 好的,即 a[] 全为偶数,那么一个奇数一定要和另一个奇数凑起来,而一次操作,会让相邻的两个 a[i]、a[i+1] 变成 a[i]+1、a[i+1]-1,即一个加,一个减。那么最小的操作数就是 两个奇数之间数量+1 。
没写出来的部分 (;′⌒`) :要求字符串最少分成几段,如果 a[i] 是偶数,ans++;如果 a[i] 是奇数,那么我们记录 a[i] 到下一个奇数 a[j] 之间所有 a 的奇偶数量,如 s="1110011000" 时,i=1; ji=2; ou=2。如果 ji+(i%2)+(j%2)==num+1 那么中间的数字都可以变成奇数,ans自然比按位+1 -1 小,同理 如果 ou+2-(i%2)-(j%2)==num+1 那么中间的数字都可以变成偶数,然后就写不出来了
样例有:
2
4
1000
4
0100
结论:我讨厌思维题 ┗|`O′|┛!!
正确:
贪心的考虑,每个段都是偶数,那我们完全可以分成长度都为 2 的小段,那么最小操作数就是所有 不同小段(小段内两个数不相同)的数量 num。
至于最少段数,
如果 小段内两个数字相同,不对 num 做出贡献,也就不对 ans 做出贡献;
如果 小段内两个数字不同,那么我们要么让他和左边的小段一样,要么和右边的小段一样 比较优(因为会少一个段),而右边的小段时一样的还是不一样的并不一定,但是左边的小段一定时一样的(因为我们从左边遍历过来的,不一样的也已经操作一样了),所以我们选择和左边的小段一样。
特殊情况:第一个小段就不一样,那他没有左边的小段,我们一直向右找,找到第一个相同的小段,就变成这个。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
const int mod = 1e9+7;
int n, ans, ans1;
char s[N];
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
s[0]=s[1]; ans=0; ans1=1;
if(s[1]!=s[2]){//如果第一个小段就不一样,向右找到第一个相同的小段
for(int i=3;i<=n;i=i+2){
if(s[i]==s[i+1]){
s[0]=s[i]; break;
}
}
}
for(int i=1;i<=n;i=i+2){
if(s[i]!=s[i+1]){
s[i]=s[i-1]; s[i+1]=s[i-1]; ans++;
}
if(s[i]!=s[i-1]){
ans1++;
}
}
cout<<ans<<" "<<ans1<<"\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int Case=1;
cin>>Case;
while(Case--) solve();
return 0;
}