在牛客网上做的一道题,加了很多注释防止自己忘记。
题目描述
玛雅人有一种密码,如果字符串中出现连续的2012四个数字就能解开密码。给一个长度为N的字符串,(2=<N<=13)该字符串中只含有0,1,2三种数字,问这个字符串要移位几次才能解开密码,每次只能移动相邻的两个数字。例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1.如果无论移位多少次都解不开密码,输出-1。
输入描述:
输入包含多组测试数据,每组测试数据由两行组成。 第一行为一个整数N,代表字符串的长度(2<=N<=13)。 第二行为一个仅由0、1、2组成的,长度为N的字符串。
输出描述:
对于每组测试数据,若可以解出密码,输出最少的移位次数;否则输出-1。
思路 :用层次遍历(bfs),儿子节点的序列是由父节点序列的其中两位进行交换得到,若不考虑排除已经检测过的序列,则当序列长度为n时,每个节点有n-1个儿子
编辑好了才发现这个例子第二层已经有合格序列了orz,但是三层比较好理解height,width1和width2,那就假装第二层的合格序列不存在吧。
#include<iostream>
#include<map>
#include<queue>
using namespace std;
string myswap(string &b,int i){
char tmp=b[i];
b[i]=b[i+1];
b[i+1]=tmp;
return b;
}
map<string,int> marked;//标记已经检测过不合格且入了队的序列,剪枝
bool judge(string s){
if(s.find("2012")>=s.size())return false;
return true;
}
int main(){
string str="";
int n;
while(cin>>n){
cin>>str;
if(judge(str)){//判断原序列,若合格则直接输出0
cout<<0<<endl;
continue;
}
marked[str]=1;//标记原序列不合格并入队
queue<string> q;
q.push(str);
//层次遍历(bfs)
int height=1;//记录层高(移位次数),原序列为第0层,此处原序列已不及格,故层高初始为1
int width1=1;//记录当前层结点数(这里的1是第0层的根节点)
int width2=0;//记录下一层结点数
bool flag=false;//用于跳出while循环
while(!q.empty()){
if(width1==0){//当前层节点已经全部出队,下一个出队的节点属于下一层,故width1和width2同时下移一层
width1=width2;//下一层的结点数赋给width1
width2=0;
height+=1;
}
str=q.front();
q.pop();
width1-=1;//当前层出队一个,width1-1
for(int i=0;i<str.size()-1;i++){
myswap(str,i);
if(marked.count(str)==0){//当前序列没有入过队
if(judge(str)){
//找到了合格序列
flag=true;
break;
}
q.push(str);//当前序列不合格
width2+=1;//下一层结点数+1
marked[str]=1;//标记当前序列已经入队
}
myswap(str, i);
}
if(flag)break;
}
if(flag)cout<<height<<endl;
else cout<<-1<<endl;
}
}