题目描述
在一个装满财宝的屋子里,有2N个盒子排成一排。除了两个相邻的空盒外,其余的每个盒子里都装有一个金球或者一个银球,总共有N-1个金球和N-1个银球。以下是一个N=5时的例子,G表示金球,S表示银球:
任意两个相邻的非空的盒子里的球可以移动到两个相邻的空盒中,移动不能改变这两个球的排列顺序。写一个程序,用最少的移动次数把所有的金球都移到所有银球的左边。
输入格式
输入文件包含多组数据。第一行为K,表示数据的总数。
每组数据的第一行是N(3<=N<=7),第二行是2N个盒子的初始状态。金球用a表示,银球用b表示,空盒用空格表示。每两组相邻数据用空行隔开。
输出格式
对于每一组数据,若无解则输出一行-1,若有解,输出最少移动次数,相邻的两组数据用一个空行隔开。
样例输入
3
3
abab
5
abba abab
6
a babbababa
样例输出
-1
3
4
解题思路
此题n<=7,显然爆搜是ok的,但是问题就是一个状态如何标记了,题目给的是字符串,一般的方法肯定是不行的,大家可以考虑哈希。但这里推出一种更神奇的方法:(仅限c++)
map!!!
map为c++stl库的一个关联函数,可以实现快速查找,复杂度为O(logn)。
map实现从键到值的映射,其效率高因为它用平衡二叉树来储存与访问 。(不用看了,反正我看不懂。)
总之呢,这个东西就可以让(bool)b[“abababab”]=1成为现实,十分好用。
那就很easy了。
代码来!!!
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>//头文件
using namespace std;
string s,s1,s2;
string f[20007];//队列的状态
int t,n,d[20007];//步数
bool bz1,bz2;
string read(){//快读
string a;char ch=getchar();
while(ch!='a'&&ch!='b'&&ch!=' ')ch=getchar();
while(ch=='a'||ch=='b'||ch==' '){a+=ch;ch=getchar();}
return a;
}
void bfs(){
int h=0,t=1,tot;
map<string,bool>b;//定义
b[s]=1;
f[t]=s;
d[t]=0;
while(h<t){
s1=f[++h];
bz1=bz2=true;
for(int j=0;j<s1.length();j++){
if(s1[j]=='b')bz1=false;
if(!bz1&&s1[j]=='a'){bz2=false;break;}
}
if(bz2){printf("%d\n",d[h]);return;}
//这一段查看是否符合了题意(金在银左边)
for(int i=1;i<s1.length();i++){
//搜索下一个状态,这里设i,i-1为移动的两个盒子的位置
if(s1[i]==' '||s1[i-1]==' ')continue;//为空就换下一个
s2="";//s2为更新后的状态
tot=0;
for(int j=0;j<=i-2;j++)
if(s1[j]!=' ')s2+=s1[j];
else {
if(tot==0)s2+=s1[i-1],tot++;
else s2+=s1[i];
}
s2+=" ";
tot=0;
for(int j=i+1;j<s1.length();j++)
if(s1[j]!=' ')s2+=s1[j];
else {
if(tot==0)s2+=s1[i-1],tot++;
else s2+=s1[i];
}
//上面为求s2的过程,若有大佬会用函数更快那小弟也只能膜拜
if(b[s2])continue;//判断有没有出现过
b[s2]=1;
t++;
f[t]=s2;
d[t]=d[h]+1;
//入队过程
}
}
printf("-1\n");//不可能达到
return;
}
int main(){
scanf("%d",&t);
while(t!=0){
t--;
scanf("%d",&n);
s=read();//读入
bfs();//宽搜
}
}
同样使用map的一道例题的网址:c++STL库map的运用——昵称
END!!!
THANK YOU!!!