问题描述
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。
然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。
当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。
输入两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。输出至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。
样例输入
011 000
样例输出
1分析:通过枚举来解决这个问题,通过对问题分析,首先可以得到两个结论:1.对于一个按钮,实际上只有两个状态:开/关,对应一个按钮 按/不按,即每个按钮最多只按一次2.各个按钮被按下的顺序对最终的结果没有影响总体思路:我们最终的目的是让两组按钮序列对应相等,等价于所有相对应的两个按钮相等因此,我们可以约定一个顺序,比如从左到右分析,依次比较每个字符,如果相等则比较下一个,如果不等,有两种方法可以翻转状态:一是按下当前按钮,二是按下下一个按钮。对于前者,同时翻转前一个按钮(除去一个特殊情况,见下方),对于后者,对前一个按钮不影响。*特殊情况:当第一个按钮不同时,如果采取第一个翻转办法,对前一个(实际不存在)按钮无影响,而从第二个按钮开始,如果还采取第一种方法,对前一个按钮会有影响,因此,从第二个按钮开始必须采用翻转后一个按钮的办法。*说明:从上述总体思路可知,如果从第二个按钮开始,仍要采取第一个翻转方法的话,必然破坏了前面按钮的一致性,如果就此继续分析,要恢复前面按钮的一致性,不外乎三种方法其一,按下前面按钮的左边,其二,按下前面按钮,其三,按下当前按钮(此时当前按钮已经按了两次)对于第一种办法,问题的规模又开始向着原始规模恢复,无法得到解对于第二种办法,可能规模向着原始规模恢复,而且重置了后一个按钮的状态综上,得到结论:除了第一个按钮,其他所有按钮要改变其状态,只能通过其后的按钮状态来改变其状态(对应于从左到右分析的约定)简言之:对于第一个按钮,分类讨论:1.按下当前按钮 2.按下下一个按钮对于其他按钮,只能是按下下一个按钮代码如下:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 void Flipone(char& s); //翻转单个元素 6 int Getmin(int*); //取最小次数 7 8 int cnt[2] = { 0 }; 9 10 int main() 11 { 12 char a[35], b[35], c[35]; 13 cin >> a >> b; 14 int len = strlen(a); 15 for (int p = 0; p < 2; ++p) { //两种情况分别讨论 16 strcpy(c, a); 17 if (p == 1) { 18 Flipone(c[0]); 19 Flipone(c[1]); 20 cnt[p]++; 21 if (strcmp(b, c) == 0) 22 goto flag; 23 } 24 for (int i = 0; i < len - 1; ++i) { 25 if (c[i] != b[i]) { 26 Flipone(c[i]); 27 Flipone(c[i+1]); 28 if (i+2 < len) { 29 Flipone(c[i + 2]); 30 } 31 cnt[p]++; 32 if (strcmp(b, c) == 0) 33 goto flag; 34 } 35 } 36 } 37 flag: 38 int cnt_min = Getmin(cnt); 39 if (strcmp(b, c) == 0) 40 cout << cnt_min << endl; 41 else 42 cout << "impossible" << endl; 43 } 44 45 void Flipone(char& s) 46 { 47 if (s == '0') 48 s = '1'; 49 else 50 s = '0'; 51 } 52 53 int Getmin(int* cnt) 54 { 55 if (cnt[0] > cnt[1]) 56 return cnt[1]; 57 else 58 return cnt[0]; 59 }
*注意:不建议使用goto语句