九宫重拍(bfs + 康拓展开)

问题描述

  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入格式

  输入第一行包含九宫的初态,第二行包含九宫的终态。

输出格式

  输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678.
123.46758

样例输出

3

样例输入

13524678.
46758123.

样例输出

22

==========================分割线==================================

这个题刚开始直接用bfs去做,但是开的标记数组的维数会非常高,后来才在网上看到用康拓展开,能用到康拓展开是因为这可以看成是一个序列,所以可以用康拓展开求出他在全排列中的次序,这样标记数组就可以开一维的了,这道题的广搜和三个水杯那个题差不多

代码如下:

  1 #include<iostream>
  2 #include <cstdio>
  3 #include <queue>
  4 #include <cstring>
  5 using namespace std;
  6 typedef long long LL;
  7 struct Node{
  8     int cur[9];
  9     LL step;
 10 };
 11 Node s, e;
 12 const int N = 1e6;
 13 const int Next[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};//搜索的四个方向
 14 bool vis[N * 4];//标记数组
 15 int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//前几个数的阶乘
 16 LL cantor(int s[])//康拓展开
 17 {
 18     LL ans = 0;
 19     int n = 9;
 20     for (int i = 0; i < n - 1; i++)
 21     {
 22         int tmp = 0;
 23         for (int j = i + 1; j < n; j++)
 24             if (s[j] < s[i])
 25                 tmp++;
 26         ans += fac[n - i - 1] * tmp;
 27     }
 28     return ans;
 29 }
 30 void cantor_reverse(int index, int a[])//康拓展开逆, 在本道题中未使用
 31 {
 32     index--;
 33     int n = 9;
 34     bool visit[9];
 35     memset(visit, false, sizeof(visit));
 36     for (int i = 0; i < n; i++)
 37     {
 38         int tmp = index / fac[n - i - 1];
 39         for (int j = 0; j <= tmp; j++)
 40             if (visit[j])
 41                 tmp++;
 42         a[i] = tmp + 1;
 43         visit[tmp] = true;
 44         index %= fac[n - i - 1];
 45     }
 46 }
 47 bool ischecked(int row, int col)//检查是否满足移动的条件
 48 {
 49     return (row > 0 && col > 0 && row < 4 && col < 4);
 50 }
 51 bool matched(Node node)//看是否达到给定的状态
 52 {
 53     for (int i = 0; i < 9; i++)
 54         if (node.cur[i] != e.cur[i])
 55             return false;
 56     return true;
 57 }
 58 LL bfs()
 59 {
 60     memset(vis, false, sizeof(vis));
 61     queue<Node> Q;
 62     s.step = 0;
 63     Q.push(s);
 64     Node p, q;
 65     int start_num = cantor(s.cur);
 66     vis[start_num] = true;//标记第一个元素
 67     while (!Q.empty())
 68     {
 69         p = Q.front();
 70         Q.pop();
 71         int pos;
 72         for (pos = 0; pos < 9; pos++)
 73             if (p.cur[pos] == 9)//将"."当成9来计算
 74                 break;
 75         int row, col, new_row, new_col;
 76         row = pos / 3 + 1;
 77         col = pos % 3 + 1;
 78         for (int i = 0; i < 4; i++)
 79         {
 80             new_row = row + Next[i][0];
 81             new_col = col + Next[i][1];
 82             if (ischecked(new_row, new_col))//判断是否满足可移动的条件
 83             {
 84                 q = p;
 85                 q.step = p.step + 1;
 86                 //下面三步是交换这两个数(也就是移动到空位去)
 87                 int t = q.cur[(row - 1) * 3 + col - 1];
 88                 q.cur[(row - 1) * 3 + col - 1] = q.cur[(new_row - 1) * 3 + new_col - 1];
 89                 q.cur[(new_row - 1) * 3 + new_col - 1] = t;
 90                 if (matched(q))//如果找到之后直接返回
 91                 {
 92                     return q.step;
 93                 }
 94                 int num = cantor(q.cur);
 95                 if (!vis[num])
 96                 {
 97                     vis[num] = true;
 98                     Q.push(q);
 99                 }
100             }
101         }
102     }
103     return -1;//找不到就返回-1
104 }
105 int main()
106 {
107     char sta[10], en[10];
108     scanf("%s %s", sta, en);
109     for (int i = 0; i < 9; i++)
110         if (sta[i] != ‘.‘)
111             s.cur[i] = sta[i] - ‘0‘;
112         else
113             s.cur[i] = 9;//将‘.‘看成9
114     for (int i = 0; i < 9; i++)
115         if (en[i] != ‘.‘)
116             e.cur[i] = en[i] - ‘0‘;
117         else
118             e.cur[i] = 9;
119     LL tmp = bfs();
120     printf("%lld\n", tmp);
121
122     return 0;
123 }

时间: 2024-10-07 14:44:25

九宫重拍(bfs + 康拓展开)的相关文章

UVA 10085(bfs+康拓展开)八数码问题

Description Problem A The Most Distant State Input: standard input Output: standard output The 8-puzzle is a square tray in which eight square tiles are placed. The remaining ninth square is uncovered. Each tile has a number on it. A tile that is adj

九宫重排_康拓展开_bfs

历届试题 九宫重排 时间限制:1.0s   内存限制:256.0MB 问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可以移动到空格中.经过若干次移动,可以形成第二个图所示的局面. 我们把第一个图的局面记为:12345678. 把第二个图的局面记为:123.46758 显然是按从上到下,从左到右的顺序记录数字,空格记为句点. 本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达.如果无论多少步都无法到达,则输出-1. 输入格

ACM/ICPC 之 BFS(离线)+康拓展开 (HDU1430-魔板)

魔板问题,一道经典的康拓展开+BFS问题,为了实现方便,我用string类来表示字符串,此前很少用string类(因为不够高效,而且相对来说我对char数组的相关函数比较熟),所以在这里也发现了很多容易被忽视的问题. 对于康拓展开不太熟系的可以先参看一篇博客:http://blog.csdn.net/zhongkeli/article/details/6966805 关于sting类,大家要注意,在赋值的时候,其赋值位置不能与首位置间存在未赋值部分. 题目需要转换思路的地方是: 我们需要将起始魔

Hdoj 1430 魔板 【BFS】+【康拓展开】+【预处理】

魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2139    Accepted Submission(s): 452 Problem Description 在魔方风靡全球之后不久,Rubik先生发明了它的简化版--魔板.魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示.任一时刻魔板的状态可用方块的颜

hdu 1043 Eight (八数码问题)【BFS】+【康拓展开】

<题目链接> 题目大意:给出一个3×3的矩阵(包含1-8数字和一个字母x),经过一些移动格子上的数后得到连续的1-8,最后一格是x,要求最小移动步数. 解题分析:本题用BFS来寻找路径,为了降低复杂度,用BFS从最终的目标状态开始处理,将所有搜索到状态以及对应的路径打表记录,然后对于输入的矩阵,直接查表输出答案 即可,本题还有一个难点,就是如何判断记录已经搜索过的状态,如果使用map+string(矩阵看成一维)会超时,所以我们这里用康拓展开式来记录状态. #include<cstdio

【HDOJ3567】【预处理bfs+映射+康拓展开hash】

http://acm.hdu.edu.cn/showproblem.php?pid=3567 Eight II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 130000/65536 K (Java/Others)Total Submission(s): 4541    Accepted Submission(s): 990 Problem Description Eight-puzzle, which is also calle

hdoj1043 Eight(逆向BFS+打表+康拓展开)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 思路: 由于自己对康拓展开用的太少,看到这个题没想到康拓展开,最开始打算直接转换为数字,但太占内存了,又想到可以将状态存进set,后来查了一下发现原来是考察康拓展开.另外就是需要打表预处理,这样快很多.BFS部分就是已知终点,从终点逆向搜索,并存每个状态的上一个状态以及操作,以便输出. 坑点:输入是多组输入,POJ那道题才是一组输入,卡在这一上午T_T. 有一组输入为12345678x,需要特

BFS+康托展开(洛谷1379 八数码难题)

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入格式: 输入初试状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据) 输入样例#1: 2831

康拓展开与逆康拓展开

1.康托展开的解释 康托展开就是一种特殊的哈希函数 把一个整数X展开成如下形式: X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1! 其中,a为整数,并且0<=a<i,i=1,2,..,n {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个.123 132 213 231 312 321 . 代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来. 他们间的对应关系可由康托展开来找到.