记录66
#include<bits/stdc++.h>
using namespace std;
int main(){
int cnt=0;
string s;
cin>>s;
int len=s.size()-1;
for(int i=0;i<len;i++){
if(s[i]=='0'){
cnt+=2;
while(s[i]==s[i+1]) i++;
}
}
if(s[len-1]=='1'&&s[len]=='0') cnt+=2;
if(s[0]=='0') cout<<cnt-1;
else cout<<cnt;
return 0;
}
题目传送门
https://www.luogu.com.cn/problem/P2708
突破口
有很多个硬币摆在一行,有正面朝上的,也有背面朝上的。正面朝上的用 1 表示,背面朝上的用 0 表示。
现在要求从这行的第一个硬币开始,将从第一个硬币开始的前若干个硬币同时翻面,求如果要将所有硬币翻到正面朝上,最少要进行这样的操作多少次?
思路
找到反转的规律
例如:1000
因为三个0,000反转一次变成111
所以可以看成是10的情况一样两次
反转一次,0111
再次反转,1111
通过例子,可以发现,每遇到一个或者一坨0的时候,就要反转两次
继续尝试会发现,如果是0开头的话,会少一次
例如:0001
即01的情况
反转一次,1111
借助这两种情况开始编码
代码简析
#include<bits/stdc++.h>
using namespace std;
int main(){
int cnt=0;
string s;
cin>>s;
int len=s.size()-1;
for(int i=0;i<len;i++){
if(s[i]=='0'){
cnt+=2;
while(s[i]==s[i+1]) i++;
}
}
if(s[len-1]=='1'&&s[len]=='0') cnt+=2;
if(s[0]=='0') cout<<cnt-1;
else cout<<cnt;
return 0;
}
int cnt=0;(反转次数)
string s;(硬币情况)
int len=s.size()-1; 👉 要统计后面的跟当前是否一样,所以最多统计到倒数第二个
for(int i=0;i<len;i++){} 👉 从第一个到到数第二个
if(s[i]=='0'){} 👉 遇到0了
cnt+=2; 👉 计数器+2
while(s[i]==s[i+1]) i++; 👉 后面是0就一直跳过这一坨0
if(s[len-1]=='1'&&s[len]=='0') cnt+=2; 👉 一直到倒数第二个,如果尾巴是10的加上最后一个0
因为到倒数第二个,所以尾巴的情况是四种:
01:while可以统计到
11:不用统计
00: 这一坨0看成一个整体
10: while会忽略到最后一个0,所以要多加一次2
if(s[0]=='0') cout<<cnt-1; 👉 思路分析过了,如果开头有的一坨或一个0,只计数一次,所以减1
else cout<<cnt; 👉 去掉两种特殊情况后的普遍情况
补充
CSP-J 硬币翻转题型解题技巧总结
硬币翻转是 CSP-J 中高频的思维 + 模拟类题型,核心考查奇偶性分析、贪心策略和状态推导,题目通常以 “翻转硬币改变状态(正 / 反),求最少操作次数或判断是否有解” 的形式出现。
一、核心本质:奇偶性决定状态
这是所有硬币翻转题的根本依据,必须牢记:
- 硬币只有 2 种状态(正 / 反,可抽象为
0/1)。- 对一个硬币翻转奇数次,状态改变;翻转偶数次,状态不变。
- 操作的顺序不影响最终结果(先翻 A 再翻 B,和先翻 B 再翻 A,效果相同)。
基于这一点,我们可以把问题简化为:统计每个硬币需要翻转的次数的奇偶性,而非具体次数。
二、常见题型与对应技巧
CSP-J 中硬币翻转题主要分为 一维线性 和 二维网格 两类,难度从易到难,技巧各有侧重。
题型 1:一维线性硬币翻转(最常考)
题目特征:硬币排成一行,翻转规则通常为「翻转第
i枚硬币时,会连带翻转其相邻的硬币(如左边 / 右边 / 左右两边)」,要求将所有硬币翻到目标状态(全正 / 全反),求最少操作次数或判断无解。核心技巧:贪心策略(从左到右推导)贪心的逻辑:前面的硬币状态确定后,后面的操作就唯一确定了,无需回头调整。具体步骤:
- 状态抽象:用
0表示正面,1表示反面(或反之),将硬币状态存储为数组a[],目标状态为target[](通常全0或全1)。- 从左到右遍历:从第 1 枚硬币遍历到第
n-1枚(n为总硬币数)。
- 若当前硬币
a[i]与目标状态不符,必须执行翻转操作(因为后面的操作无法影响前面的硬币状态)。- 执行翻转:根据题目规则,翻转当前硬币
i,并翻转其右侧的硬币i+1(若规则是翻当前和右边)。- 记录操作次数。
- 边界检查(判断无解):遍历结束后,检查最后一枚硬币的状态是否符合目标。
- 若符合:操作次数即为答案。
- 若不符合:无解(因为最后一枚硬币无法再被翻转,翻转它会影响右边,但右边没有硬币)。
例题场景
有 n 枚硬币排成一行,初始状态为
0/1串,每次可以翻转第 i 枚硬币和第 i+1 枚硬币,求将所有硬币翻为全 0 的最少操作次数,若无解输出 -1。思路:从左到右遍历,遇到1就翻转当前和下一个,最后检查第 n 枚是否为0。注意点
- 翻转规则不同,操作对象不同(如翻当前和左边,则从右到左贪心)。
- 操作次数需计数,且要避免重复翻转。
题型 2:二维网格硬币翻转(进阶题型)
题目特征:硬币排成
n*m的网格,翻转规则为「翻转第(i,j)枚硬币时,会翻转其上下左右 4 个相邻硬币(十字形翻转)」,要求将网格翻为全目标状态。核心技巧:状态压缩 + 枚举第一行由于二维网格的操作会影响上下行,无法直接贪心,因此采用「枚举第一行状态 + 推导后续行」的策略,步骤如下:
- 状态抽象:用二维数组
grid[][]存储硬币状态,0正1反。- 枚举第一行的翻转状态:
- 第一行有
m枚硬币,每枚硬币有「翻 / 不翻」2 种选择,总共有2^m种可能(m ≤ 10时枚举可行,CSP-J 中m不会太大)。- 用位运算枚举:例如
mask从0到2^m -1,mask的二进制第k位为1表示翻转第一行第k枚硬币。- 推导后续行的翻转操作:
- 从第 2 行到第
n行遍历,对于第i行第j列的硬币:
- 观察上一行(
i-1行)第j列的硬币状态:如果它不符合目标状态,必须翻转当前(i,j)位置的硬币(因为只有当前行的操作能影响上一行的状态)。- 翻转
(i,j)时,连带翻转其上下左右的硬币。- 检查最后一行状态:
- 遍历完所有行后,检查第
n行的所有硬币是否符合目标状态。- 若符合:记录当前操作次数,取所有可行方案的最小值。
- 若不符合:此枚举方案无效,跳过。
注意点
- 枚举第一行时,用位运算可以大幅提升效率,避免超时。
- 翻转操作要写一个通用函数(传入坐标,翻转自身和相邻),减少代码冗余。
三、通用解题步骤(适用于所有硬币翻转题)
- 状态建模:将硬币的正反转换为
0/1数组,明确初始状态和目标状态。- 分析翻转规则:确定翻转一个硬币会影响哪些位置(自身 / 相邻 / 十字形)。
- 选择策略:一维用贪心,二维用「枚举第一行 + 推导后续」。
- 判断无解情况:一维看最后一枚硬币,二维看最后一行硬币。
- 代码实现:
- 用
0/1存储状态,翻转操作等价于a[i] ^= 1(异或运算,简洁高效)。- 贪心遍历要注意边界,枚举要注意位运算的细节。
四、易错点与避坑指南
- 混淆翻转规则:题目中翻转可能影响左边 / 右边 / 上下左右,必须严格按照规则写翻转逻辑,否则全盘皆错。
- 忽略奇偶性:若初始状态与目标状态的差异数为奇数,总操作次数必须为奇数;反之则为偶数,可快速排除部分无解情况。
- 超时问题:二维枚举时,
m超过 15 时2^m会很大,CSP-J 中不会出现此类情况,放心枚举。- 边界处理:一维的最后一枚硬币、二维的最后一行,是判断无解的关键,务必检查。
五、典型例题思路(CSP-J 难度)
题目类型 核心思路 一维翻当前 + 右边,全翻为正 从左到右贪心,遇到反面就翻,最后检查最后一枚 二维十字翻转,全翻为正 枚举第一行翻转状态,推导后续行,检查最后一行 给定操作次数,判断能否达成目标 分析奇偶性,计算需要翻转的次数是否匹配
392

被折叠的 条评论
为什么被折叠?



