魔板问题的分析与解答

来源:互联网 发布:核辐射是什么东西 知乎 编辑:程序博客网 时间:2024/05/17 01:09

题目来源:SJTU 4013

Description

描述

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

对于每种可能的状态,这三种基本操作都可以使用。 你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。

Input Format

只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间)不换行,表示目标状态。

Output Format

第一行: 包括一个整数,表示最短操作序列的长度。 第二行: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7 BCABCCB

Limits

时间限制:1000ms

=======================我是华丽的分割线=======================

注意要点

1、数据输入是顺时针的,正确把数据放入数组后,后续的三种操作都可以正常进行

2、题目要求的是从基本状态走向目标状态,而非还原操作

我的分析

1、需要构建一个类保存信息,包含2个数据成员:走到这一步最短的步骤,该数组的康托展开

2、需要康托函数和逆康托函数,将数组整合为一个数表示当前状态,配合散列表快速查找该状态是否已经抵达过

3、ABC三种操作分别封装为函数,读入一个康托数,解析成数组进行变换,然后封装为康托数return

4、利用队列进行广度优先搜索

#include <iostream>#include <cstring>#include <string>#include <queue>using namespace std;int cantor(int arr[]); // 康托函数void uncantor(int x,int arr[]); // 逆康托函数// 三种操作函数,读入一个康托数int A(int a);int B(int a);int C(int a);// 存放阶乘int fac[9];struct moban{string s; // 记录步骤int c; // 康托数moban(string a="", int b=-1):s(a), c(b) {} // 康托数从0开始,用-1避免冲突};int main(){queue <moban> q; // 广度优先搜索队列int i, j, k, t[8], r=0, zhun[]={1, 2, 3, 4, 8, 7, 6 ,5}, biao;// i, j, k 循环变量 r 步数// t[8]记录输入的数据// zhun[]开始的基本状态 biao 存储基本状态对应的康托数bool flag = true, hash[50000] = {false};// 计算阶乘,为康托函数服务,背得出的也可以自己直接写fac[0] = 1;    for (i=1; i<=8; ++i)fac[i] = fac[i-1] * i;// 读入数据,注意顺时针for (i=0; i<4; ++i)cin >> t[i];for (i=7; i>3; --i)cin >> t[i];// 计算目标康托数biao = cantor(t);// 将基本状态入队moban a("", cantor(zhun));q.push(a);while (flag) // 开始搜索{r++;int m = q.size();for (i=0; i<m && flag; ++i){moban tmp = q.front();q.pop();if (tmp.c == biao){flag = false;string res = tmp.s;int le = res.size(), hang = le/60; // 计算输出行数cout << r-1 << endl;for (k=0; k<hang+1; ++k) // 每行60个字符{for (j=0; j<60 && le>0; ++j){le--;cout << res[60*hang+j];}cout << endl;}}if (flag){int a1=A(tmp.c), a2=B(tmp.c), a3=C(tmp.c);// 判断三种操作后产生的情况是否出现过,没有则入队,并记录相应的操作步骤if (!hash[a1]){hash[a1] = true;string f=tmp.s + "A";moban x(f, a1);q.push(x);}if (!hash[a2]){hash[a2] = true;string f=tmp.s + "B";moban x(f, a2);q.push(x);}if (!hash[a3]){hash[a3] = true;string f=tmp.s + "C";moban x(f, a3);q.push(x);}}}}system("pause");return 0;}int cantor(int arr[]){    int i, j, t, ans=0;    for (i=0; i<7; ++i){        t=0;        for (j=i+1; j<=7; ++j)if (arr[j]<arr[i]) t++; // 计算逆序数        ans = (ans+t)*(7-i); // 阶乘求和简化运算,不理解的话自己拿草稿纸}    return ans;}void uncantor(int x,int arr[]){    int i,j,l,t,u[8];    memset(&u,0,sizeof(u));    for (i=0; i<=7; ++i){        t = x/fac[7-i];        x -= t*fac[7-i];        for (j=0,l=0; l<=t; ++j)if (u[j]==0) l++;        j--;        u[j] = 1;        arr[i] = j + 1;}}// 由于数据不多,直接数组定位转换int A(int a){int tmp[8], s[8];uncantor(a, tmp);s[0] = tmp[4];s[1] = tmp[5];s[2] = tmp[6];s[3] = tmp[7];s[4] = tmp[0];s[5] = tmp[1];s[6] = tmp[2];s[7] = tmp[3];return cantor(s);}int B(int a){int tmp[8], s[8];uncantor(a, tmp);s[0] = tmp[3];s[1] = tmp[0];s[2] = tmp[1];s[3] = tmp[2];s[4] = tmp[7];s[5] = tmp[4];s[6] = tmp[5];s[7] = tmp[6];return cantor(s);}int C(int a){int tmp[8], s[8];uncantor(a, tmp);s[0] = tmp[0];s[1] = tmp[5];s[2] = tmp[1];s[3] = tmp[3];s[4] = tmp[4];s[5] = tmp[6];s[6] = tmp[2];s[7] = tmp[7];return cantor(s);}


 

原创粉丝点击