描述:
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。
然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。
当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。
解题思路:
首先,锁的状态只有两种;对同样一个锁按两次与不按是一样的。
思路如下:
1.我们需要求一个既定的序列。而操作的顺序对于结果没有影响,因此我们从前往后依次枚举,但一个一个枚举太耗费时间
2.分类讨论可知,对第一个锁而言,他要么自己改变状态,要么被他后面的锁改变状态,据此我们分两种情况
3.不论是以上的哪一种情况,我们都要使第一个锁变成目标状态,那么在其成为目标状态之后,第二个锁的状态就不能由他自己改变,否则同时也会造成第一个锁的状态改变,反而使我们的进度倒退
4.那么,第二个锁就只能由他后面的锁的状态改变来改变,而这个规则对后面所有的锁都同理
由此我们可以得出我们的算法:对第一个锁分两种情况讨论,然后再去遍历之后的锁并改变其状态,如果第一个锁的两种情况都不能得出目标序列,那么就输出impossible
代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int mintimes = 30;
string s1;
cin >> s1;
vector<bool> lock;
for (int i = 0; i < s1.size(); i++)
{
lock.push_back(s1[i] - '0');
}
vector<bool> init = lock;
vector<bool> target;
string s2;
cin >> s2;
for (int i = 0; i < s2.size(); i++)
{
target.push_back(s2[i] - '0');
}
for (int i = 0; i < 2; i++)//对第一个锁的状态分类讨论
{
lock = init;//恢复初始状态
int times = 0;
if (i == 1)
{
lock[0] = !lock[0];
lock[1] = !lock[1];
times++;
}
for (int j = 0; j < lock.size() - 1; j++)
{
if (lock[j] != target[j])
{
lock[j] = !lock[j];
lock[j + 1] = !lock[j + 1];
if (j + 1 != lock.size() - 1)
lock[j + 2] = !lock[j + 2];
times++;
}
}
if (target == lock)
mintimes = min(times, mintimes);
}
if (mintimes == 30)
cout << "impossible";
else cout << mintimes;
}