#10027. 「一本通 1.4 例 2」魔板

题目

题目描述

原题来自:USACO 3.2.5

Rubik 先生在发明了风靡全球魔方之后,又发明了它的二维版本——魔板。这是一张有 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 之间)不换行,表示目标状态。

输出格式

输出文件的第一行包括一个整数,表示最短操作序列的长度。 第二行为在字典序中最早出现的操作序列。

样例

输入

2 6 8 4 5 7 3 1

输出

7
BCABCCB

代码详解

#include<bits/stdc++.h>
using namespace std;
struct mb{
	string ans;
	char xl[8];
}fir;
char fin[8];
queue<mb> q;
unordered_set<int> ss;
mb A(mb);
mb B(mb);
mb C(mb);
bool vis(mb);
void bfs();
int main()
{
	fir.ans="";
	for(char i='1';i<='8';i++){
		fir.xl[i-'0'-1]=i;
	}
	for(int i=0;i<8;i++){
		cin>>fin[i];
		getchar();
	}
	q.push(fir); 
	bfs();
	return 0;
}
mb A(mb t){
	mb tt;
	tt.ans=t.ans+'A';
	for(int i=0;i<8;i++){
		tt.xl[i]=t.xl[7-i];
	}
	return tt;
}
mb B(mb t){
	mb tt;
	tt.ans=t.ans+'B';
	tt.xl[0]=t.xl[3];
	tt.xl[1]=t.xl[0];
	tt.xl[2]=t.xl[1];
	tt.xl[3]=t.xl[2];
	tt.xl[4]=t.xl[5];
	tt.xl[5]=t.xl[6];
	tt.xl[6]=t.xl[7];
	tt.xl[7]=t.xl[4];
	return tt;
}
mb C(mb t){
	mb tt;
	tt.ans=t.ans+'C';
	for(int i=0;i<8;i++){
		tt.xl[i]=t.xl[i];
	}
	tt.xl[1]=t.xl[6];
	tt.xl[2]=t.xl[1];
	tt.xl[5]=t.xl[2];
	tt.xl[6]=t.xl[5];
	return tt;
}
bool vis(mb t){
	int l;
	sscanf(t.xl,"%d",&l);
	if(!ss.count(l)){
		ss.emplace(l);
		return true;
	}
	return false;
}
bool check(mb t){
	for(int i=0;i<8;i++){
		if(t.xl[i]!=fin[i]){
			return false;
		}
	}
	return true;
}
void bfs(){
	while(!q.empty()){
		mb now=q.front();
		q.pop();
		if(check(now)){
			cout<<now.ans.size()<<endl<<now.ans;
			return;
		}
		mb tmp=A(now);
		if(vis(tmp)){
			q.push(tmp);
		}
		tmp=B(now);
		if(vis(tmp)){
			q.push(tmp);
		}
		tmp=C(now);
		if(vis(tmp)){
			q.push(tmp);
		}
	}
}

操作模拟

mb A(mb t){
	mb tt;
	tt.ans=t.ans+'A';//保留答案数组
	for(int i=0;i<8;i++){
		tt.xl[i]=t.xl[7-i];
	}
	return tt;
}
mb B(mb t){
	mb tt;
	tt.ans=t.ans+'B';
	tt.xl[0]=t.xl[3];
	tt.xl[1]=t.xl[0];
	tt.xl[2]=t.xl[1];
	tt.xl[3]=t.xl[2];
	tt.xl[4]=t.xl[5];
	tt.xl[5]=t.xl[6];
	tt.xl[6]=t.xl[7];
	tt.xl[7]=t.xl[4];
	return tt;
}
mb C(mb t){
	mb tt;
	tt.ans=t.ans+'C';
	for(int i=0;i<8;i++){
		tt.xl[i]=t.xl[i];
	}
	tt.xl[1]=t.xl[6];
	tt.xl[2]=t.xl[1];
	tt.xl[5]=t.xl[2];
	tt.xl[6]=t.xl[5];
	return tt;
}

相信读者能够读明白ABC三种操作是怎么实现的,笔者在这不再赘述

康托展开

unordered_set<int> ss;
bool vis(mb t){
	int l;
	sscanf(t.xl,"%d",&l);//把字符串转换成整数
	if(!ss.count(l)){//判断在ss中有没有存过l
		ss.emplace(l);//在里面添加一个l
		return true;
	}
	return false;
}

bfs

void bfs(){
	while(!q.empty()){
		mb now=q.front();//取队头
		q.pop();
		if(check(now)){//如果找到答案
			cout<<now.ans.size()<<endl<<now.ans;
			return;
		}
		mb tmp=A(now);//进行ABC操作
		if(vis(tmp)){
			q.push(tmp);
		}
		tmp=B(now);
		if(vis(tmp)){
			q.push(tmp);
		}
		tmp=C(now);
		if(vis(tmp)){
			q.push(tmp);
		}
	}
}

end

总的来说,这是一道经典的广搜题
需要用到的知识是Hash判重
但是我们直接用特别优美、特别完美的康托展开逃课
相信读者能够读懂主函数部分,笔者在这里不多赘述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值