sicily1151魔板
来源:互联网 发布:软件导刊是普刊吗 编辑:程序博客网 时间:2024/05/25 19:57
sicily1151 魔板解题报告
1. 原题中文大意
由8个大小相同方块组成的魔板,初始状态是1 2 3 4
8 7 65
对魔板可以进行三种操作,分别是A操作:上下行互换,B操作:每次以行循环右移一个), C操作(中间四小块顺时钟转一格)。题目有多组测试数据,每组数据输入第一行是最多容许步数,第二三行是目标状态,输出为操作的步数及操作序列。
2. 算法思想及主要用到的数据结构
这是一道典型的搜索题,给出一种初始状态,要求搜索到目标状态,可以用深度优先搜索或广度优先搜索来解决,即初始状态为搜索树的根节点,然后有ABC操作就有了三个孩子节点,每个孩子节点又有ABC操作,因此这是一颗完全三叉树,当扩展到目标状态时则这颗子树不用扩展下去了。如果是深度优先搜索的话,很可能要遍历整棵搜索树,考虑到问题的规模,用深搜不太妥当,肯定会超时。用广搜比较合适,但是如果只用单纯的广搜也很可能会超时,因为有很多状态会重复搜索,肯定会浪费很多时间,因此需要进行判重,当搜索要重复状态时,则不进行扩展。对于判重,可以用STL的set或者用康托展开,在搜索过程中,记录相关操作信息即可。
用到的数据结构:主要用到队列,数组。
3. 详细解题思路
可以存储每个状态对应的康托编码对应的操作序列来求解,即把所有状态先搜索完。
首先定义一个数据结构,储存当前状态(用一个int型整数来表示)和操作序列(用string表示),再定义这样的一个结构体数组,大小为40321(8个数组成的8位数最多只有8!=40320个), 然后再定义一个bool型访问数组,记录哪个下标已被访问。接下来进行广搜,根节点为(12348765, ""), 把根节点放进队列,此时队列的头指针为0,尾指针为1,然后只要头指针小于尾指针,则取出队列元素,然后这个元素的一个整数分别进行A、B、C操作(这些操作可以通过对整数运算来实现,可写3个操作函数),如果操作后的整数的康托编码是没有访问过的,则构造一个新节点,新节点的操作序列为之前的操作序列再加上A或B或C, 知道队列里没有未访问的节点为止。当遇到新节点时,储存这个节点编码对应的操作序列。
最后,当程序输入时,求出这个目标状态对应的康托编码,然后输出答案或-1.
4. 逐步求精算法描述
定义结构体:
structStatus
{
int num;
string ans;
Status(int s, string a)
{
num = s;
ans = a;
}
Status(){}
}q[40321];
q是结构体数组,最多只有40320种状态。
intfac[8] = { 1, 1, 2, 6, 24, 120, 720, 5040};
fac存阶乘大小,用于求康托编码;
boolvisit[40321];
visit[i]用于判断编码为i的状态是否已访问过;
stringans[40321];
ans[i]存编码为i的操作序列;
intA(int num) { // } // A操作
intB(int num) { // } // B操作
intC(int num) { //} // C操作
inthashCode(int num){ // return n; }
把状态为num进行康托展开,返回映射成的整数;
voidbfs() // 广搜
{ }//把所有节点状态对应的操作序列储存起来
5. 逐程序注释清单
#include<cstdio>#include<cstring>#include<string>#include<set>#include<queue>#include<iostream>#include<algorithm> usingnamespace std; intn, target;inta[8];boolvisit[40321];//visit[i]用于判断编码为i的状态是否已访问过;stringans[40321];//ans[i]存编码为i的操作序列;//fac存阶乘大小,用于求康托编码;intfac[8] = { 1, 1, 2, 6, 24, 120, 720, 5040}; structStatus{ int num; string ans; Status(int s, string a) { num = s; ans = a; } Status(){}}q[40321];//结构体数组,用于储存每种状态的数值和操作序列,最多有//40320种状态 intA(int num) // A操作,上下行交换{ int t = 0; t += num % 10000 * 10000; t += num / 10000; return t;} intB(int num) //B操作,每行循环右移一位{ int t= 0; t += num / 10000 % 10 * 10000000; t += num / 100000 * 10000; t += num % 10000 / 10; t += num % 10 * 1000; return t;} intC(int num) //C操作,中间四小块顺时钟转一格{ int t = 0; t += num / 10000000 * 10000000; t += num / 1000 % 100 * 1000; t += num % 10; t += num / 100 % 10 * 1000000; t += num % 100 / 10 * 100; t += num / 100000 % 10 * 10; t += num / 1000000 % 10 * 100000; return t;} /* 把数值为num映射成一个整数,比如12345678对应0,因为由1-8组成的8 *位数中12345678最小,问题转化为求解有多少个整数比这个数小,这就是康 *托展开*/inthashCode(int num){ int n, cnt, i; n = 0; //1-8组成的8位数中比num小的数的个数 i = 7; int a[8]; while(num != 0)//把整数num各个位的数值存在a数组中 { a[i--] = num % 10; num /= 10; } for(i = 0; i < 8; ++i) { cnt = 0; //cnt记录后面的数有多少个数比这个小 for(int j = i + 1; j < 8; ++j) { if(a[i] > a[j]) cnt++; } n += fac[7 - i] * cnt; //判断到当前位,有多少个数比num小}return n;} /* 利用队列把整个搜索树无重复的搜索遍历,记录每个编码对应的操作序列, */voidbfs() // 广搜{ Status tem; int t, code; int head, tail; memset(visit, false, sizeof(visit)); //首先设置所有的都未访问过 head = 0; //队列头指针 tail = 1; //队列尾指针 q[head] = Status(12348765, "");//根节点为(12348765,"")首先入队 visit[hashCode(q[head].num)] = true; while(head < tail) //只要队列中还有未被访问过的元素 { tem = q[head]; //取出队首元素 t = A(tem.num); //t为操作A之后对应的数值 code = hashCode(t);//code为t对应的康托编码 if(!visit[code]){ //如果编码为code的状态为访问过,则标记已访//问,把操作序列存入ans[code], 并把这个状态节点入队 visit[code] = true; ans[code] = tem.ans +"A"; q[tail++] =Status(t,tem.ans+"A"); } t = B(tem.num);//t为操作A之后对应的数值 code = hashCode(t);//code为t对应的康托编码 if(!visit[code]){//如果编码为code的状态为访问过,则标记已访//问,把操作序列存入ans[code], 并把这个状态节点入队 visit[code] = true; ans[code] = tem.ans +"B"; q[tail++] =Status(t,tem.ans+"B"); } t = C(tem.num);//t为操作A之后对应的数值 code = hashCode(t);//code为t对应的康托编码 if(!visit[code]){//如果编码为code的状态为访问过,则标记已访//问,把操作序列存入ans[code], 并把这个状态节点入队 visit[code] = true; ans[code] = tem.ans +"C"; q[tail++] =Status(t,tem.ans+"C"); } head++; //头指针向后移一位 }} intmain(){ int i; bfs(); while(scanf("%d", &n) != EOF&& n != -1) { for(i = 0; i < 8; ++i) { scanf("%d", a[i] + i); } int t = 1; target = 0; for(i = 7; i >= 0;--i) { target += t*a[i]; t *= 10; } int code = hashCode(target); if(ans[code].size() > n) cout << -1 << endl; else cout << ans[code].size()<< " " << ans[code] << endl; } return 0;}
6. 测试数据
测试数据1: 1 1 2 3 4 8 7 6 5
输出: 0
测试数据2: 2 8 76 5 1 2 3 4
输出: 1 A
测试数据3: 3 1 3 6 4 8 2 7 5
输出: 3 ACA
测试数据4: 2 1 3 6 4 8 2 7 5
输出: -1
测试数据5: 8 4 3 2 6 5 1 8 7
输出: 5 ACBCA
7. 程序优化
在上面的程序中,我使用了一次宽度优先搜索来记录搜索树上的所有状态,用康托编码来判重,这算是比较快的,因为用时0.04s,输入我用了scanf,但是输出我用了cout,这是因为我用了C++的string类型,如果我用C语言的char类型来储存操作序列的话,输出时间应该会减少,总时间应该会减到0.03,但是空间可能会增大。
- sicily1151魔板
- sicily1151&魔板2
- Sicily1150 && Sicily1151(广搜)
- 魔板
- 魔板
- 7.5 魔板
- 1151. 魔板
- sicily1511 魔板
- sicily1150 & 魔板
- 1151. 魔板
- 1151 魔板
- 魔板[S]
- 1692 魔板
- bzoj1331: 魔板
- Sicily 1151. 魔板
- HDU 1430 魔板 搜索
- 1151. 魔板[Speical judge]
- Sicily 1151. 魔板
- 【软件测试自动化-QTP系列讲座 43】== MTM多脚本执行管理器(二) 自动化模型篇==
- java web 标签库
- 当你说不行时,你应该说些什么:以Android播放midi为例
- 【读书笔记】Apache 2.0.45 目录结构
- Json官方在线文档
- sicily1151魔板
- 几个面试问题
- NHibernate + SQLite + MVC 开发记录
- 关于main函数的三个参数
- Strcat,strcpy,strcmp,Strlen函数原型
- NHibernate + SQLite + MVC 开发记录 二
- 安装mysql服务出现Install/Remove of the Service Denied!错误
- JSP的执行过程\JSP的隐式对象
- __init和__exit宏