[USACO3.2]魔板 Magic Squares
题目背景
在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
题目描述
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
下面是对基本状态进行操作的示范:
A: 8 7 6 5
1 2 3 4
B: 4 1 2 3
5 8 7 6
C: 1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
输入格式
只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间)不换行,表示目标状态。
输出格式
Line 1: 包括一个整数,表示最短操作序列的长度。
Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。
样例 #1
样例输入 #1
2 6 8 4 5 7 3 1
样例输出 #1
7
BCABCCB
提示
题目翻译来自NOCOW。
USACO Training Section 3.2
首先,请忘记康托展开,STL大法好!
大致思路:
- 这是一道很明显的bfs题,可以反过来广搜判断,基本状态什么时候可以到输入的目标状态。
- 去掉重复的,因为重复的我们之前就在更早的时候到达过了,一定不会优于之前,且会导致无限循环。
- 书上采取的办法是Hash,康托展开,实际上map更好用一点。
map用法
成员方法 | 功能 |
---|---|
map< int > m | 定义一个map |
begin() | 返回指向容器中第一个(已排好序的第一个)键值对的双向迭代器 |
end() | 返回指向容器中最后一个元素(已排好序的最后一个)所在位置的后一个位置的双向迭代器。 |
rbegin() | 返回指向容器中最后一个(已排好序的最后一个)元素的反向双向迭代器。 |
rend() | 返回指向容器中第一个(已排好序的第一个)元素所在位置的前一个位置的反向双向迭代器。 |
cbegin() | 和begin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
cend() | 和end()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
crbegin() | 和rbegin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
crend() | 和rend()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
find(key) | 在map容器中查找键为key的键值对,若成功找到,则返回指向该键值对的双向迭代器;若未找到,则返回和end()方法一样的迭代器。 |
lower_bound(key) | 返回一个指向当前map容器中第一个大于或等于key的键值对的双向迭代器。 |
upper_bound(key) | 返回一个指向当前map容器中第一个大于key的键值对的双向迭代器。 |
equal_range(key) | 返回一个pair对象(包含2个双向迭代器),其中pair.first和lower_bound()方法的返回值等价,pair.second和upper_bound()方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为key的键值对(map容器键值对唯一,因此该返回最多包含一个键值对)。 |
count() | 查找本元素是否存在 |
———————————————— |
详细见STL map详细用法
- 用map存储每个字符串,将其作为下标,其中的值存储经过了哪些操作
- 对于操作ABC,我们可以定义函数来实现他们,对应样例,不过多解释。
string A(string xx){
string x,y;
for(int i=0;i<4;i++){
x+=xx[3-i];
y+=xx[7-i];
}
xx=y+x;
return xx;
}
string B(string xx){
string x=xx;
xx[0]=x[3];xx[1]=x[0];xx[2]=x[1];xx[3]=x[2];
xx[4]=x[5];xx[5]=x[6];xx[6]=x[7];xx[7]=x[4];
return xx;
}
string C(string xx){
string x=xx;
xx[1]=x[6];xx[2]=x[1];xx[5]=x[2];xx[6]=x[5];
return xx;
}
- 剩下的就是bfs模板啦,通过队列实现一层层的搜索
AC CODE
#include<queue>
#include<bits/stdc++.h>
using namespace std;
string a,aa;
int ans=0;
map<string,string>m;
queue <string> q;
string A(string xx){
string x,y;
for(int i=0;i<4;i++){
x+=xx[3-i];
y+=xx[7-i];
}
xx=y+x;
return xx;
}
string B(string xx){
string x=xx;
xx[0]=x[3];xx[1]=x[0];xx[2]=x[1];xx[3]=x[2];
xx[4]=x[5];xx[5]=x[6];xx[6]=x[7];xx[7]=x[4];
return xx;
}
string C(string xx){
string x=xx;
xx[1]=x[6];xx[2]=x[1];xx[5]=x[2];xx[6]=x[5];
return xx;
}
int main(){
for(int i=0;i<8;i++){
char a1;
cin>>a1;
a+=a1;//字符串拼接
getchar();//读入空格
}
q.push("12345678");
m["12345678"]="";
while(!q.empty()){
aa=q.front();
string h;
h=A(aa);
if(m.count(h)==0){//如果操作A的结果没有出现过
q.push(h);//入队
m[h]=m[aa]+'A';//进行了操作A,记录
}
h=B(aa);
if(m.count(h)==0){//如果操作B的结果没有出现过
q.push(h);//入队
m[h]=m[aa]+'B';//进行了操作B,记录
}
h=C(aa);
if(m.count(h)==0){//如果操作C的结果没有出现过
q.push(h);//入队
m[h]=m[aa]+'C';//进行了操作C,记录
}
if(m.count(a)!=0){//找到答案
cout<<m[a].size()<<endl;//进行了多少操作
cout<<m[a];//进行了哪些操作
break;
}
q.pop();
}
return 0;
}