注:该题目来自第四届蓝桥杯省赛C/C++ B组
题面如下:
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:**oo***oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作。
输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。
输出格式
一个整数,表示最小操作步数
数据范围
输入字符串的长度均不超过100。
数据保证答案一定有解。
输入样例1:
**********
o****o****
输出样例1:
5
输入样例2:
*o**o***o***
*o***o**o***
输出样例2:
1
这个题非常像“费解的开关”一题,不过比那个题代码量要少不少,本题我的整体思路如下:首先,我们先用下图所示的对应关系将对相邻硬币的翻转操作归结到结点上:
(我们假设a点是这个硬币序列的开头)
接下来我们来定义一种操作,叫做“翻转操作”。
我们规定:如果a处发生一次“翻转操作”,我们就会改变a和b两枚硬币的正反;如果b处发生一次“翻转操作”,我们就会改变b和c两枚硬币的正反,以此类推
注意e处就不应该再有“翻转操作”了,因为e硬币只与d硬币相邻,而同时改变d和e两枚硬币的正反是d处发生“翻转操作”的定义,所以在上图中,实际上可能发生“翻转操作”的只有N-1处
从上图中,我们可以看到如果a处的硬币正反状态需要改变,我们必须执行a处硬币的“翻转操作”,因为没有其他地方的“翻转操作”能够改变a处硬币的状态
而一旦确定下来a处的“翻转操作”做或者不做,除了b处的“翻转操作”以外,也就再没有其他地方的“翻转操作”可以改变b处硬币的状态了,以此类推,我们可以一直类推到d处
总结来说:i处的“翻转操作”做不做取决于i-1处的“翻转操作”结束后,i处的硬币状态是否正确;即i处的“翻转操作”做与不做是出于保证i处的硬币状态正确的考虑
而对于末尾的e硬币(它是第N枚硬币,而我们的“翻转操作”,它只到第N-1处),没有后面的能保证它的状态正确了
所以如果完整考虑,我们应该判断一下e处硬币的状态是否正确:如果正确,我们就能够把硬币翻转到目标状态;否则就不能(但是题目保证有解了,所以就省事了hhh)
代码就非常简单了:O(N)扫过去就可以了
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; int main() { char c1[105]; char c2[105]; cin >> c1; cin >> c2; int length = strlen(c1); int cnt = 0; for(int i = 0; i < length - 1; i++) { if(c1[i] != c2[i]) { cnt++; if(c1[i+1] == '*') c1[i+1] = 'o'; else c1[i+1] = '*'; } } cout << cnt << endl; return 0; }