最终优化代码地址: 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. 单引号、双引号 Html转义符 ----2014年12月2日

    &apos;----单引号 "-----双引号 在一个网页中的按钮,写onclick事件的处理代码,不小心写成如下: <input value="Test" ...

  2. vector预分配空间溢出

    vector 当一个vector预分配的存储空间用完之后,为维护其连续的对象数组,它必须在另外一个地方重新分配大块新的(更大的)存储空间,并把以前已有的对象拷贝到新的存储空间中去. // A clas ...

  3. 安装ImageMagick扩展出现configure: error: not found. Please provide a path to MagickWand-config or Wand- config program

    安装ImageMagick扩展报错: checking ImageMagick MagickWand API configuration program... checking Testing /u ...

  4. 用typedef给结构体一个别名

    转:typedef 一.用typedef给结构体一个别名 typedef struct tagMyStruct { int iNum; long lLength; } MyStruct; 这语句实际上 ...

  5. Quartz1.8.5例子(三)

    /* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the ...

  6. base64的一个应用情景

    AddActivity.xml rushrank.xml 不过AddActivity.xml不也是通过二进制流就传过去了吗?      事实上是可以的,只要不将这些二进制的数据写下来,传播是可以的,只 ...

  7. MySQL在创建存储过程的时候,语法正确却提示You have an error in your SQL syntax

    我在使用MySQL工具编写MySQL存储过程的时候,明明语法正确,但是却一直提示You have an error in your SQL syntax. 比如下面一段代码 CREATE PROCED ...

  8. NHibernate configuration

    http://blog.csdn.net/dbcolor/article/details/2061929

  9. 下一代hadoop

    1,hadoop 2.0 产生背景2,hadoop 2.0 基本构成3,HDFS 2.04 YARN5 MapReduce On YARN6 Hadoop 2.0初体验7 总结 1,hadoop 2. ...

  10. Hadoop 学习笔记 (十一) MapReduce 求平均成绩

    china:张三 78李四 89王五 96赵六 67english张三 80李四 82王五    84赵六 86math张三 88李四 99王五 66赵六 77 import java.io.IOEx ...