描述
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。
然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。
当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。
输入
两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。
输出
至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。
样例输入
011 000
样例输出
1
解题分析
二进制序列,按了其中一个数可以让其和其旁边的一个或者两个数取反。
其实这道题的限制条件也很容易发现,关键就在于一个分类讨论,那就是第一个按钮开始的一个确定序列。什么意思?如果前面一个按钮按与不按的状态确定了,那么后续的按钮按与不按的状态也都确定了,我们只需要枚举第一个按钮的状态即可。这个又可以联想到一个15*15方格的画家图画板的问题,或者说是点灯问题,这也是很经典的。
我们枚举第一个按钮的状态,如果二者第一个数不同,我们要想改变第一个数的状态就只有按第一个按钮或者说按第二个按钮。如果我们选择了按第一个按钮,那么后续能够影响第二个按钮状态的就只有第三个按钮了,如果不按第一个按钮,我们就得按第二个按钮,然后我们同样地也可以发现能影响第二个按钮的就只有第三个按钮了,以此类推。
代码实现
#include <iostream>
#include <cmath>
#include <iomanip>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <bitset>
using namespace std;
void press(string &s,int i){
int l=s.size();
if(i==0){
s[i]=(s[i]=='1'?'0':'1');
s[i+1]=(s[i+1]=='1'?'0':'1');
}
else if(i==l-1){
s[i]=(s[i]=='1'?'0':'1');
s[i-1]=(s[i-1]=='1'?'0':'1');
}
else{
s[i]=(s[i]=='1'?'0':'1');
s[i-1]=(s[i-1]=='1'?'0':'1');
s[i+1]=(s[i+1]=='1'?'0':'1');
}
}
int main(){
string cur,des;
int len;
int c=0;
int res=1e9;
cin>>cur>>des;
len=cur.size();
//分类讨论,第一个位置按或者不按,总共只有这两种情况
//之后的每个位置都因为第一个位置按或者不按而确定
string cur1=cur;
press(cur,0);
c++;
for(int i=1;i<len;i++){
if(cur[i-1]==des[i-1]){
continue;
}
else{
press(cur,i);
c++;
}
}
if(cur==des){
res=min(res,c);
}
c=0;
for(int i=1;i<len;i++){
if(cur1[i-1]==des[i-1]){
continue;
}
else{
press(cur1,i);
c++;
}
}
if(cur1==des){
res=min(res,c);
}
if(res==1e9){
cout<<"impossible"<<endl;
}
else{
cout<<res<<endl;
}
return 0;
}