[蓝桥杯 2013 省 B] 翻硬币
1.题目
题目背景
小明正在玩一个“翻硬币”的游戏。
题目描述
桌上放着排成一排的若干硬币。我们用 *
表示正面,用 o
表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo
,如果同时翻转左边的两个硬币,则变为 oooo***oooo
。现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
输入格式
两行等长字符串,分别表示初始状态和要达到的目标状态,每行长度小于 \(1000\)。
数据保证一定存在至少一种方案可以从初始状态和要达到的目标状态。
输出格式
一个整数,表示最小操作步数。
样例 #1
样例输入 #1
**********
o****o****
样例输出 #1
5
样例 #2
样例输入 #2
*o**o***o***
*o***o**o***
样例输出 #2
1
提示
source:蓝桥杯 2013 省 B 组 H 题
2. 题解
2.1 贪心算法
思路
这里最关键的是理解,能够将硬币翻至与所需一致所需要的条件:
1.如果只有一个(奇数个,在消除偶数个硬币剩下一个)硬币不一致,首先这个硬币是必须翻的,如果翻了,就会导致另一个硬币不一致,永远无法到达最终状态!!!
所以不一致状态必须是偶数个。
2.如果是偶数个硬币不一致,我们就要考虑翻牌方法,这里必须翻两个相邻的硬币;
比如像100110001,我们希望将其全部翻至000000...的状态。
根据1,我们可以发现必须是两两相消的这种才能真正改变状态, 比如像从1001->0101->0011->0000, 这里我们必须先出两个1,才能达到从左到右一次次翻转达到最终状态
也就是,翻动次数其实是由两个1之间的距离所决定的,而总的翻动次数是由我们所有两个1选择组合的距离之和
那这样问题就变成了我们如何选择这里的1了,我们也可以说先从中间的两个11开始翻转?
我们可以比较一下,如果是从头到尾开始翻转那么是一个1001 和 10001, 为 3 + 4 = 7
但是从中间翻转是一个 11 和 10000(11翻转成00)0001, 为 1 + 8 = 9,
我们可以明显发现,如果想要整体翻转次数最少,我们要避免任何的11组合发生交叉(如果有交叉就会有重复,次数就会增多)
3.因此一种最优的贪心算法,就是从头开始找状态不符合的两个最近的1,并进行翻转,这样就永远不会发生交叉,而导致重复。
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
string s1, s2;
cin >> s1 >> s2;
int ans = 0;
for(int i = 0; i < s1.length(); i++){
if(s1[i] != s2[i]){
s1[i+1] = s1[i+1] == 'o' ? '*':'o';
ans++;
}
}
cout << ans;
}