最终优化代码地址: https://github.com/laiy/Datastructure-Algorithm/blob/master/sicily/1151.c

题目如下

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB , Special Judge

Description

魔板由8个大小相同方块组成,分别用涂上不同颜色,用1到8的数字表示。

其初始状态是

1 2 3 4

8 7 6 5

对魔板可进行三种基本操作:

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

输入包括多个要求解的魔板,每个魔板用三行描述。

第一行步数N(不超过10的整数),表示最多容许的步数。

第二、第三行表示目标状态,按照魔板的形状,颜色用1到8的表示。

当N等于-1的时候,表示输入结束。

Output

对于每一个要求解的魔板,输出一行。

首先是一个整数M,表示你找到解答所需要的步数。接着若干个空格之后,从第一步开始按顺序给出M步操作(每一步是A、B或C),相邻两个操作之间没有任何空格。

注意:如果不能达到,则M输出-1即可。

Sample Input

4
5 8 7 6
4 1 2 3
3
8 7 6 5
1 2 3 4
-1

Sample Output

2 AB
1 A 评分:M超过N或者给出的操作不正确均不能得分。

Problem Source

ZSUACM Team Member

此题难度不大,重要的是怎么优化,更快效率,更少内存使用。

一开始看到这题,典型的搜索题目。

没多想写出如下平凡的BFS搜索代码:

 #include <cstdio>
#include <string>
#include <iostream>
#include <set>
#include <queue> int n, destination, top, bottom, a, b, c, d;
std::string ans;
std::set<int> visited; struct State {
int state;
std::string step;
State(int state, std::string step) {
this->state = state;
this->step = step;
}
}; int OP_A(int state) {
return state / + state % * ;
} int OP_B(int state) {
top = state / ;
bottom = state % ;
top = top % * + top / ;
bottom = bottom % * + bottom / ;
return top * + bottom;
} int OP_C(int state) {
a = state / % ;
b = state / % ;
c = state / % ;
d = state / % ; state -= a * ;
state -= b * ;
state -= c * ;
state -= d * ; state += c * ;
state += a * ;
state += d * ;
state += b * ; return state;
} void bfs() {
int state = ;
if (state == destination) {
ans = "";
return;
} std::queue<State> q;
q.push(State(state, ""));
while (!q.empty()) {
State s = q.front();
q.pop(); if (visited.find(s.state) != visited.end())
continue;
if (s.step.length() > (size_t)n)
break;
if (s.state == destination) {
ans = s.step;
break;
} visited.insert(s.state); q.push(State(OP_A(s.state), s.step + "A"));
q.push(State(OP_B(s.state), s.step + "B"));
q.push(State(OP_C(s.state), s.step + "C"));
}
} int main() {
while (scanf("%d", &n) && n != -) { ans = "NOT FOUND";
destination = ;
visited.clear(); int temp[], base = , i;
for (i = ; i < ; i++)
scanf("%d", temp + i);
for (i = ; i >= ; i--)
destination += temp[i] * base, base *= ; bfs(); if (ans == "NOT FOUND")
printf("-1\n");
else
std::cout << ans.length() << " " << ans << std::endl;
}
return ;
}

轻轻松松过了,时间0.4s,内存2300kb。

时间明显慢的不能忍,看排行榜少数人最佳的是0.00s!

于是开始我们的优化过程:

首先联想到,这里用的set数据结构来记录搜索中访问过的节点,而我们对set的使用基本不会涉及交集并集的操作,只是单纯的访问这个元素在不在。

那么有个基础常识需要注意:STL中set是用红黑树实现的,此时对元素的查找是远不如哈希快的,所以我们把set替换成hash_set将会大幅提高效率。

即把原来的set声明改成hash_set即可:

 __gnu_cxx::hash_set<int> visited;

简单的改动, 时间0.25s,内存2428kb。

还是不够快!

于是继续修改,舍弃hash_set,改为visited数组线性访问,但是内存会爆! 8^8的空间!

用康托展开减少记录空间,即8^8空间压到8!空间大小,此时内存不会爆。

代码如下:

 #include <cstdio>
#include <string>
#include <iostream>
#include <queue>
#include <cstring> int n, destination, top, bottom, a, b, c, d, base, i, j;
std::string ans;
int fact[] = {, , , , , , , , };
bool visited[]; struct State {
int state;
std::string step;
State(int state, std::string step) {
this->state = state;
this->step = step;
}
}; int encode(int n)
{
static int tmp[];
for (i = ; i >= ; i--) {
tmp[i] = n % ;
n /= ;
} for (i = ; i < ; i++) {
base = ;
for (j = i + ; j < ; j++)
if (tmp[i] > tmp[j]) base++;
n += fact[ - i] * base;
} return n;
} int OP_A(int state) {
return state / + state % * ;
} int OP_B(int state) {
top = state / ;
bottom = state % ;
top = top % * + top / ;
bottom = bottom % * + bottom / ;
return top * + bottom;
} int OP_C(int state) {
a = state / % ;
b = state / % ;
c = state / % ;
d = state / % ; state -= a * ;
state -= b * ;
state -= c * ;
state -= d * ; state += c * ;
state += a * ;
state += d * ;
state += b * ; return state;
} void bfs() {
int state = ;
if (state == destination) {
ans = "";
return;
} std::queue<State> q;
q.push(State(state, ""));
while (!q.empty()) {
State s = q.front();
q.pop(); if (visited[encode(s.state)])
continue;
if (s.step.length() > (size_t)n)
break;
if (s.state == destination) {
ans = s.step;
break;
} visited[encode(s.state)] = true; q.push(State(OP_A(s.state), s.step + "A"));
q.push(State(OP_B(s.state), s.step + "B"));
q.push(State(OP_C(s.state), s.step + "C"));
}
} int main() {
while (scanf("%d", &n) && n != -) { ans = "NOT FOUND";
destination = ;
memset(visited, , sizeof(visited)); int temp[], base = , i;
for (i = ; i < ; i++)
scanf("%d", temp + i);
for (i = ; i >= ; i--)
destination += temp[i] * base, base *= ; bfs(); if (ans == "NOT FOUND")
printf("-1\n");
else
std::cout << ans.length() << " " << ans << std::endl;
}
return ;
}

时间0.22s,空间1404kb。

效果并没有比hash_set快多少!

那么问题出在哪里呢?

博主想了一天(边上课边想),想到答案:

每个样例都在搜索同一棵树!!!!!

这样会产生大量的重复搜索,在不同的样例之间,特别是testcase很多的情况下,这是多余的。

所以,我们要把整棵树记录下来。

再看我们的数据结构,之前用的整数来存储一个节点状态,但是这里操作ABC效率是低下的,我们不如用3位来存储一个数字,这样ABC操作直接用位运算来完成。

然后只遍历一次搜索树,把每个节点对应的操作记录下来,然后在具体需要的时候回溯逆向输出就是结果,这里考虑到用数组来记录节点状态的话内存开销太大1<<24的大小不是个小数字,所以用的是hash_map来记录树。

代码如下:

 #include <cstdio>
#include <queue>
#include <hash_map> inline int OP_A(int &n) {
return (n & ) << | n >> ;
} inline int OP_B(int &n) {
return (( << | << ) & n) >> | (~( << | << ) & n) << ;
} inline int OP_C(int &n) {
return (( | << | << | << ) & n) | (( << ) & n) << | (( << ) & n) << | (( << ) & n) >> | (( << ) & n) >> ;
} inline int resume_B(int &n) {
return (( | << ) & n) << | (~( | << ) & n) >> ;
} inline int resume_C(int &n) {
return (( | << | << | << ) & n) | (( << ) & n) << | (( << ) & n) >> | (( << ) & n) << | (( << ) & n) >> ;
} inline int zip(int *a) {
static int code;
code = ;
for (int i = ; i < ; ++i)
code |= (a[i] - ) << ( * i);
return code;
} int a[] = {, , , , , , , }, origin = zip(a); __gnu_cxx::hash_map<int, char> record; void bfs() {
int temp, code;
std::queue<int> q;
q.push(origin);
while (!q.empty()) {
code = q.front();
q.pop();
temp = OP_A(code);
if (!record[temp])
record[temp] = 'A', q.push(temp);
temp = OP_B(code);
if (!record[temp])
record[temp] = 'B', q.push(temp);
temp = OP_C(code);
if (!record[temp])
record[temp] = 'C', q.push(temp);
}
} int main() {
bfs();
int n, arr[], i, j;
char s[];
while (scanf("%d", &n) && n != -) {
for (i = ; i < ; i++)
scanf("%d", arr + i);
for (i = zip(arr), j = ; i != origin && j < n; j++) {
s[j] = record[i];
switch (s[j]) {
case 'A':
i = OP_A(i);
break;
case 'B':
i = resume_B(i);
break;
case 'C':
i = resume_C(i);
break;
}
}
if (i != origin)
printf("-1\n");
else {
printf("%d ", j);
while (j--)
putchar(s[j]);
putchar('\n');
}
}
return ;
}

时间0.01s,空间1120kb。

至于最优的0.00s,本人能力有限实在想不出来了,若有大神赐教定将顶礼膜拜。

Sicily1151:魔板搜索及优化的更多相关文章

  1. 【搜索】魔板问题(BFS)

    [搜索]魔板问题 时间限制: 1 Sec  内存限制: 64 MB提交: 5  解决: 3[提交][状态][讨论版] 题目描述 据说能使持有者成为世界之主的上古神器隐藏在魔板空间,魔板由8个同样大小的 ...

  2. Sicily 1150: 简单魔板(BFS)

    此题可以使用BFS进行解答,使用8位的十进制数来储存魔板的状态,用BFS进行搜索即可 #include <bits/stdc++.h> using namespace std; int o ...

  3. HDU 1430 魔板(康托展开+BFS+预处理)

    魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submis ...

  4. hdu1430魔板(BFS+康托展开)

    做这题先看:http://blog.csdn.net/u010372095/article/details/9904497 Problem Description 在魔方风靡全球之后不久,Rubik先 ...

  5. hdu1430魔板

    Problem Description 在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板.魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示.任一时刻魔板的状态可 ...

  6. Sicily 1151 魔板

    Constraints Time Limit: 1 secs, Memory Limit: 32 MB , Special Judge Description 魔板由8个大小相同方块组成,分别用涂上不 ...

  7. 【题解】Luogu P2730 魔板

    蒟蒻的第一道蓝题--好像也没有蓝的程度 一篇无STL的超弱题解(入门写法无误了QAQ 传送门 很经典的一道BFS 这是初始状态. 操作A 操作B 操作C 思路1 不使用cantor展开的情况 1. 对 ...

  8. [洛谷P2730] 魔板 Magic Squares

    洛谷题目链接:魔板 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都 ...

  9. 洛谷 P2730 魔板 Magic Squares

    P2730 魔板 Magic Squares 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 ...

随机推荐

  1. MSSQL备份及数据迁移

    版本:MSSQL 2008 备份情景:从A服务器的SQL 迁移到B服务器,并且数据也迁移过去. 操作环境:A服务器:WINDOWS7  B服务器:WINDOWS8.1   辅助工具:VNC 首先从A服 ...

  2. 系统设计 - 使用面向 iOS 的本机插件扩展

    本文转自:http://www.cnblogs.com/zhwl/archive/2013/07/26/3217155.html 本文细致探讨了 Xcode(以 iOS 设备为目标)中的 PhoneG ...

  3. PHP 学习笔记 (三)

    stream_context_create()函数的用法: $url = "http://www.someweb.com" $context = stream_context_cr ...

  4. 洛谷 P1336 最佳课题选择

    P1336 最佳课题选择 题目提供者 yeszy 标签 动态规划 福建省历届夏令营 传送门 难度 尚无评定 题目描述 Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择.由于课 ...

  5. a-b(高精度)

    我现在已经是才语言中的一员了,我在此献上今日的佳作——a-b(高精度),以下是我的程序及其注释,欢迎各位来观赏,耶! 程序: #include<stdio.h> #include<s ...

  6. NLPIR.user Not valid license or your license expired! Please feel free to contact pipy_zhang@msn.com

    NLPIR.user Not valid license or your license expired! Please feel free to contact pipy_zhang@msn.com ...

  7. MAC 使用Jetbrains's产品

    Jetbrains's MAC 使用 ./gradle fatjar 或者 ./gradlew.sh fatjar java -jar build/lib/xx.jar 链接: http://pan. ...

  8. 练习2 I题 - 水仙花数

      Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   Description 春天是 ...

  9. keil对51单片机变量和函数的编译处理

    (1)初始值不是0的全局变量 在程序调到main()函数执行前,除了要进行内存清零.初始化堆栈外,还需要将全局变量的初始值加载到RAM的指定区域(编译过程中为全局变量分配的空间). (2)未初始化的局 ...

  10. uboot环境变量区为何不能放在data段

    一.疑问 环境变量也是全局变量,为何不能像其他的全局变量放在data段呢?为什么要放在堆中或者使用ENV_IS_EMBEDDED定义的CFG_ENV_SIZE的空间大小,又为什么需要这么大的空间呢? ...