最终优化代码地址: 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

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

Sample Output

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

Problem Source

ZSUACM Team Member

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

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

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

  1. #include <cstdio>
  2. #include <string>
  3. #include <iostream>
  4. #include <set>
  5. #include <queue>
  6.  
  7. int n, destination, top, bottom, a, b, c, d;
  8. std::string ans;
  9. std::set<int> visited;
  10.  
  11. struct State {
  12. int state;
  13. std::string step;
  14. State(int state, std::string step) {
  15. this->state = state;
  16. this->step = step;
  17. }
  18. };
  19.  
  20. int OP_A(int state) {
  21. return state / + state % * ;
  22. }
  23.  
  24. int OP_B(int state) {
  25. top = state / ;
  26. bottom = state % ;
  27. top = top % * + top / ;
  28. bottom = bottom % * + bottom / ;
  29. return top * + bottom;
  30. }
  31.  
  32. int OP_C(int state) {
  33. a = state / % ;
  34. b = state / % ;
  35. c = state / % ;
  36. d = state / % ;
  37.  
  38. state -= a * ;
  39. state -= b * ;
  40. state -= c * ;
  41. state -= d * ;
  42.  
  43. state += c * ;
  44. state += a * ;
  45. state += d * ;
  46. state += b * ;
  47.  
  48. return state;
  49. }
  50.  
  51. void bfs() {
  52. int state = ;
  53. if (state == destination) {
  54. ans = "";
  55. return;
  56. }
  57.  
  58. std::queue<State> q;
  59. q.push(State(state, ""));
  60. while (!q.empty()) {
  61. State s = q.front();
  62. q.pop();
  63.  
  64. if (visited.find(s.state) != visited.end())
  65. continue;
  66. if (s.step.length() > (size_t)n)
  67. break;
  68. if (s.state == destination) {
  69. ans = s.step;
  70. break;
  71. }
  72.  
  73. visited.insert(s.state);
  74.  
  75. q.push(State(OP_A(s.state), s.step + "A"));
  76. q.push(State(OP_B(s.state), s.step + "B"));
  77. q.push(State(OP_C(s.state), s.step + "C"));
  78. }
  79. }
  80.  
  81. int main() {
  82. while (scanf("%d", &n) && n != -) {
  83.  
  84. ans = "NOT FOUND";
  85. destination = ;
  86. visited.clear();
  87.  
  88. int temp[], base = , i;
  89. for (i = ; i < ; i++)
  90. scanf("%d", temp + i);
  91. for (i = ; i >= ; i--)
  92. destination += temp[i] * base, base *= ;
  93.  
  94. bfs();
  95.  
  96. if (ans == "NOT FOUND")
  97. printf("-1\n");
  98. else
  99. std::cout << ans.length() << " " << ans << std::endl;
  100. }
  101. return ;
  102. }

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

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

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

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

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

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

  1. __gnu_cxx::hash_set<int> visited;

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

还是不够快!

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

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

代码如下:

  1. #include <cstdio>
  2. #include <string>
  3. #include <iostream>
  4. #include <queue>
  5. #include <cstring>
  6.  
  7. int n, destination, top, bottom, a, b, c, d, base, i, j;
  8. std::string ans;
  9. int fact[] = {, , , , , , , , };
  10. bool visited[];
  11.  
  12. struct State {
  13. int state;
  14. std::string step;
  15. State(int state, std::string step) {
  16. this->state = state;
  17. this->step = step;
  18. }
  19. };
  20.  
  21. int encode(int n)
  22. {
  23. static int tmp[];
  24. for (i = ; i >= ; i--) {
  25. tmp[i] = n % ;
  26. n /= ;
  27. }
  28.  
  29. for (i = ; i < ; i++) {
  30. base = ;
  31. for (j = i + ; j < ; j++)
  32. if (tmp[i] > tmp[j]) base++;
  33. n += fact[ - i] * base;
  34. }
  35.  
  36. return n;
  37. }
  38.  
  39. int OP_A(int state) {
  40. return state / + state % * ;
  41. }
  42.  
  43. int OP_B(int state) {
  44. top = state / ;
  45. bottom = state % ;
  46. top = top % * + top / ;
  47. bottom = bottom % * + bottom / ;
  48. return top * + bottom;
  49. }
  50.  
  51. int OP_C(int state) {
  52. a = state / % ;
  53. b = state / % ;
  54. c = state / % ;
  55. d = state / % ;
  56.  
  57. state -= a * ;
  58. state -= b * ;
  59. state -= c * ;
  60. state -= d * ;
  61.  
  62. state += c * ;
  63. state += a * ;
  64. state += d * ;
  65. state += b * ;
  66.  
  67. return state;
  68. }
  69.  
  70. void bfs() {
  71. int state = ;
  72. if (state == destination) {
  73. ans = "";
  74. return;
  75. }
  76.  
  77. std::queue<State> q;
  78. q.push(State(state, ""));
  79. while (!q.empty()) {
  80. State s = q.front();
  81. q.pop();
  82.  
  83. if (visited[encode(s.state)])
  84. continue;
  85. if (s.step.length() > (size_t)n)
  86. break;
  87. if (s.state == destination) {
  88. ans = s.step;
  89. break;
  90. }
  91.  
  92. visited[encode(s.state)] = true;
  93.  
  94. q.push(State(OP_A(s.state), s.step + "A"));
  95. q.push(State(OP_B(s.state), s.step + "B"));
  96. q.push(State(OP_C(s.state), s.step + "C"));
  97. }
  98. }
  99.  
  100. int main() {
  101. while (scanf("%d", &n) && n != -) {
  102.  
  103. ans = "NOT FOUND";
  104. destination = ;
  105. memset(visited, , sizeof(visited));
  106.  
  107. int temp[], base = , i;
  108. for (i = ; i < ; i++)
  109. scanf("%d", temp + i);
  110. for (i = ; i >= ; i--)
  111. destination += temp[i] * base, base *= ;
  112.  
  113. bfs();
  114.  
  115. if (ans == "NOT FOUND")
  116. printf("-1\n");
  117. else
  118. std::cout << ans.length() << " " << ans << std::endl;
  119. }
  120. return ;
  121. }

时间0.22s,空间1404kb。

效果并没有比hash_set快多少!

那么问题出在哪里呢?

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

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

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

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

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

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

代码如下:

  1. #include <cstdio>
  2. #include <queue>
  3. #include <hash_map>
  4.  
  5. inline int OP_A(int &n) {
  6. return (n & ) << | n >> ;
  7. }
  8.  
  9. inline int OP_B(int &n) {
  10. return (( << | << ) & n) >> | (~( << | << ) & n) << ;
  11. }
  12.  
  13. inline int OP_C(int &n) {
  14. return (( | << | << | << ) & n) | (( << ) & n) << | (( << ) & n) << | (( << ) & n) >> | (( << ) & n) >> ;
  15. }
  16.  
  17. inline int resume_B(int &n) {
  18. return (( | << ) & n) << | (~( | << ) & n) >> ;
  19. }
  20.  
  21. inline int resume_C(int &n) {
  22. return (( | << | << | << ) & n) | (( << ) & n) << | (( << ) & n) >> | (( << ) & n) << | (( << ) & n) >> ;
  23. }
  24.  
  25. inline int zip(int *a) {
  26. static int code;
  27. code = ;
  28. for (int i = ; i < ; ++i)
  29. code |= (a[i] - ) << ( * i);
  30. return code;
  31. }
  32.  
  33. int a[] = {, , , , , , , }, origin = zip(a);
  34.  
  35. __gnu_cxx::hash_map<int, char> record;
  36.  
  37. void bfs() {
  38. int temp, code;
  39. std::queue<int> q;
  40. q.push(origin);
  41. while (!q.empty()) {
  42. code = q.front();
  43. q.pop();
  44. temp = OP_A(code);
  45. if (!record[temp])
  46. record[temp] = 'A', q.push(temp);
  47. temp = OP_B(code);
  48. if (!record[temp])
  49. record[temp] = 'B', q.push(temp);
  50. temp = OP_C(code);
  51. if (!record[temp])
  52. record[temp] = 'C', q.push(temp);
  53. }
  54. }
  55.  
  56. int main() {
  57. bfs();
  58. int n, arr[], i, j;
  59. char s[];
  60. while (scanf("%d", &n) && n != -) {
  61. for (i = ; i < ; i++)
  62. scanf("%d", arr + i);
  63. for (i = zip(arr), j = ; i != origin && j < n; j++) {
  64. s[j] = record[i];
  65. switch (s[j]) {
  66. case 'A':
  67. i = OP_A(i);
  68. break;
  69. case 'B':
  70. i = resume_B(i);
  71. break;
  72. case 'C':
  73. i = resume_C(i);
  74. break;
  75. }
  76. }
  77. if (i != origin)
  78. printf("-1\n");
  79. else {
  80. printf("%d ", j);
  81. while (j--)
  82. putchar(s[j]);
  83. putchar('\n');
  84. }
  85. }
  86. return ;
  87. }

时间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. java开发规范总结_代码编码规范

    规范需要平时编码过程中注意,是一个慢慢养成的好习惯 1.基本原则 强制性原则:     1.字符串的拼加操作,必须使用StringBuilder:     2.try…catch的用法 try{ }c ...

  2. 【转】.Net中通过反射技术的应用----插件程序的开发入门

    转自:http://www.cnblogs.com/winloa/archive/2012/03/25/2416355.html .Net中通过反射技术的应用----插件程序的开发入门 再开始之前,先 ...

  3. ubuntu1404安装配置java环境(jdk8)

    这个安装比较简单,网上也有数不清的教学,我这里记录以下,方便以后万一失忆了回来看看能想起来.个人博客http://www.cnblogs.com/wdfwolf3/ 1.下载安装 言归正传,我们需要到 ...

  4. freeswitch 拨号时添加自定义变量

    Using Channel Variables in Dialplan Condition Statements Channel variables can be used in conditions ...

  5. Global & Local Variable in Python

    Following code explain how 'global' works in the distinction of global variable and local variable. ...

  6. 完美方案——iOS的WebView自适应内容高度

    /////////////////////////////初始化,self.view是父控件///////////////////////////////// _webView = [[UIWebVi ...

  7. Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)【转载】

    Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一) 自从搞好了单向一对一关系,装满代码的心中塞进了挥之不去的情丝 —— 单相思.谁都知道音乐世界离不开情感,可谁又知 ...

  8. objective-C: nonatomic retain copy assgin 等属性详解

    http://my.oschina.net/u/728866/blog/90798 property,可以提供的功能有:提供成员变量的访问方法的声明.控制成员变量的访问权限.控制多线程时成员变量的访问 ...

  9. MySQL创建用户、授权等

    用于MySQL5.6命令行执行成功: create database wp_people; create user wp_people@'localhost' identified by 'yrwb' ...

  10. 初识Vim

    在Windows系统安装Vim后桌面上会添加gVim.gVim Easy.gVim Read-only 三个快捷方式. gVim 指向主程序,gVim Easy.gVim Read-only 也是,但 ...