P2708 硬币翻转

记录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 中高频的思维 + 模拟类题型,核心考查奇偶性分析贪心策略状态推导,题目通常以 “翻转硬币改变状态(正 / 反),求最少操作次数或判断是否有解” 的形式出现。

一、核心本质:奇偶性决定状态

这是所有硬币翻转题的根本依据,必须牢记:

  1. 硬币只有 2 种状态(正 / 反,可抽象为 0/1)。
  2. 对一个硬币翻转奇数次,状态改变;翻转偶数次,状态不变。
  3. 操作的顺序不影响最终结果(先翻 A 再翻 B,和先翻 B 再翻 A,效果相同)。

基于这一点,我们可以把问题简化为:统计每个硬币需要翻转的次数的奇偶性,而非具体次数。

二、常见题型与对应技巧

CSP-J 中硬币翻转题主要分为 一维线性二维网格 两类,难度从易到难,技巧各有侧重。

题型 1:一维线性硬币翻转(最常考)

题目特征:硬币排成一行,翻转规则通常为「翻转第 i 枚硬币时,会连带翻转其相邻的硬币(如左边 / 右边 / 左右两边)」,要求将所有硬币翻到目标状态(全正 / 全反),求最少操作次数或判断无解。

核心技巧:贪心策略(从左到右推导)贪心的逻辑:前面的硬币状态确定后,后面的操作就唯一确定了,无需回头调整。具体步骤:

  1. 状态抽象:用 0 表示正面,1 表示反面(或反之),将硬币状态存储为数组 a[],目标状态为 target[](通常全 0 或全 1)。
  2. 从左到右遍历:从第 1 枚硬币遍历到第 n-1 枚(n 为总硬币数)。
    • 若当前硬币 a[i] 与目标状态不符,必须执行翻转操作(因为后面的操作无法影响前面的硬币状态)。
    • 执行翻转:根据题目规则,翻转当前硬币 i,并翻转其右侧的硬币 i+1(若规则是翻当前和右边)。
    • 记录操作次数。
  3. 边界检查(判断无解):遍历结束后,检查最后一枚硬币的状态是否符合目标。
    • 若符合:操作次数即为答案。
    • 若不符合:无解(因为最后一枚硬币无法再被翻转,翻转它会影响右边,但右边没有硬币)。

例题场景

有 n 枚硬币排成一行,初始状态为 0/1 串,每次可以翻转第 i 枚硬币和第 i+1 枚硬币,求将所有硬币翻为全 0 的最少操作次数,若无解输出 -1。思路:从左到右遍历,遇到 1 就翻转当前和下一个,最后检查第 n 枚是否为 0

注意点

  • 翻转规则不同,操作对象不同(如翻当前和左边,则从右到左贪心)。
  • 操作次数需计数,且要避免重复翻转。
题型 2:二维网格硬币翻转(进阶题型)

题目特征:硬币排成 n*m 的网格,翻转规则为「翻转第 (i,j) 枚硬币时,会翻转其上下左右 4 个相邻硬币(十字形翻转)」,要求将网格翻为全目标状态。

核心技巧:状态压缩 + 枚举第一行由于二维网格的操作会影响上下行,无法直接贪心,因此采用「枚举第一行状态 + 推导后续行」的策略,步骤如下:

  1. 状态抽象:用二维数组 grid[][] 存储硬币状态,01 反。
  2. 枚举第一行的翻转状态
    • 第一行有 m 枚硬币,每枚硬币有「翻 / 不翻」2 种选择,总共有 2^m 种可能(m ≤ 10 时枚举可行,CSP-J 中 m 不会太大)。
    • 位运算枚举:例如 mask02^m -1mask 的二进制第 k 位为 1 表示翻转第一行第 k 枚硬币。
  3. 推导后续行的翻转操作
    • 从第 2 行到第 n 行遍历,对于第 i 行第 j 列的硬币:
      • 观察上一行(i-1 行)第 j的硬币状态:如果它不符合目标状态,必须翻转当前 (i,j) 位置的硬币(因为只有当前行的操作能影响上一行的状态)。
      • 翻转 (i,j) 时,连带翻转其上下左右的硬币。
  4. 检查最后一行状态
    • 遍历完所有行后,检查第 n 行的所有硬币是否符合目标状态。
    • 若符合:记录当前操作次数,取所有可行方案的最小值。
    • 若不符合:此枚举方案无效,跳过。

注意点

  • 枚举第一行时,用位运算可以大幅提升效率,避免超时。
  • 翻转操作要写一个通用函数(传入坐标,翻转自身和相邻),减少代码冗余。

三、通用解题步骤(适用于所有硬币翻转题)

  1. 状态建模:将硬币的正反转换为 0/1 数组,明确初始状态和目标状态。
  2. 分析翻转规则:确定翻转一个硬币会影响哪些位置(自身 / 相邻 / 十字形)。
  3. 选择策略:一维用贪心,二维用「枚举第一行 + 推导后续」。
  4. 判断无解情况:一维看最后一枚硬币,二维看最后一行硬币。
  5. 代码实现
    • 0/1 存储状态,翻转操作等价于 a[i] ^= 1(异或运算,简洁高效)。
    • 贪心遍历要注意边界,枚举要注意位运算的细节。

四、易错点与避坑指南

  1. 混淆翻转规则:题目中翻转可能影响左边 / 右边 / 上下左右,必须严格按照规则写翻转逻辑,否则全盘皆错。
  2. 忽略奇偶性:若初始状态与目标状态的差异数为奇数,总操作次数必须为奇数;反之则为偶数,可快速排除部分无解情况。
  3. 超时问题:二维枚举时,m 超过 15 时 2^m 会很大,CSP-J 中不会出现此类情况,放心枚举。
  4. 边界处理:一维的最后一枚硬币、二维的最后一行,是判断无解的关键,务必检查。

五、典型例题思路(CSP-J 难度)

题目类型核心思路
一维翻当前 + 右边,全翻为正从左到右贪心,遇到反面就翻,最后检查最后一枚
二维十字翻转,全翻为正枚举第一行翻转状态,推导后续行,检查最后一行
给定操作次数,判断能否达成目标分析奇偶性,计算需要翻转的次数是否匹配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值