Codeup 问题 D: 【宽搜入门】魔板

题目描述
在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有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个字符。

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7
BCABCCB 

题目有点坑。
坑一:是求由基本状态转换为目标状态(给出的状态)的最小步数,而不是由目标状态(给出的状态)转换为基本状态的最小步数。
坑二:题目输出的序列转换但二维数组中并不是按常规方法转换的。
比如基本状态:

1 2 3 4 5 6 7 8

转换到二维数组中为:

1 2 3 4
8 7 6 5

虽然这么说了,但到时候你大概率还是会犯这个错误(无奈ing)。

技巧一:对ABC三种操作,我将操作数放入数组op中(详见代码及其注释),然后bfs时循环进行交换操作。

技巧二:记录操作需要使用vector,并且将vector放入保存状态的结构体中,保证它和状态紧密联系。判重还是使用比较好用的map。

接下来贴出代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
using namespace std;
//关键的技巧,每两个元素分别对应ABC操作所需要交换的位置,-1代表这个操作结束,顺序不能改变,尤其是B操作。其中op数组的一维代表A操作,二三维依次为B、C操作 
int op[3][12] = { {0,4,1,5,2,6,3,7,-1,-1,-1,-1},{3,2,7,6,2,1,6,5,1,0,5,4},{2,1,1,5,5,6,-1,-1,-1,-1,-1,-1} };
char opp[3] = { 'A','B','C' };//bfs中的for循环的i映射为ABC字符 
int aim = 0;//目标状态 
map<int, bool> m;//记录以及存在的状态 
struct node {
	int num, step;//状态,以及步数 
	vector<char> v;//记录这一条线的操作,方便找到答案后输出操作 
	node(int _num, int _step, vector<char> _v) {
		num = _num;
		step = _step;
		v = _v;
	}
};
queue<node> q;
void swap(char* c, int a, int b) {//交换字符串c的a、b位置 
	char t = c[a];
	c[a] = c[b];
	c[b] = t;
}
int main() {

	int a = 0, begin = 12348765, temp[4] = { 0 };//注意我的begin不是12345678,因为这样方便我们放到数组中思考 
	char aa[10];
	vector<char> v;
	while(scanf("%d",&a)==1){
		aim=0;
		aim+=a;
		for (int i = 1; i < 8; i++) {//这里两个for循环得到aim,注意我对aim的处理 
			scanf("%d", &a);
			if (i >= 4) {
				temp[7 - i] = a;
				continue;
			}
			aim *= 10;
			aim += a;
		}
		for (int i = 0; i < 4; i++) {
			aim *= 10;
			aim += temp[i];
		}
		q.push(node(begin, 0, v));//push基础状态 
		m[begin] = 1;
		while (!q.empty()) {//不空则继续 
			int op1 = 0, op2 = 0, tnum = 0;
			char cur[10], cur_t[10];
			node temp = q.front();
			q.pop();
			if (temp.num == aim) {
				printf("%d\n", temp.step);
				for (int i = 0; i < temp.v.size(); i++) {
					printf("%c", temp.v[i]);
				}
				printf("\n");
			}
			sprintf(cur, "%d", temp.num);
			strcpy(cur_t, cur);//cur_t数组方便之后将cur复原 
			for (int i = 0; i < 3; i++) {
				for (int j = 0; j < 12; j += 2) {//内层for循环执行这一种操作所需要交换的位置 
					op1 = op[i][j], op2 = op[i][j + 1];
					if (op1 == -1) break;//当操作数是-1时,代表已经执行完交换操作 
					swap(cur, op1, op2);
				}
				sscanf(cur, "%d", &tnum);
				if (m.count(tnum) == 0) {
					m[tnum] = 1;
					temp.v.push_back(opp[i]);//将这一次操作放入v中 
					q.push(node(tnum, temp.step + 1, temp.v));//存入 
					temp.v.pop_back();//将temp中的v还原,以便进行下一个操作 
				}
				strcpy(cur, cur_t);//复原cur 
			}
		}
		while(!q.empty()) q.pop();//接下来的就是善后工作,以方便写一个测试数据可以正常处理 
		m.clear();
	}

	return 0;
}

都看到这了,不妨点个赞吧!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值