Aizu 0121 Seven Puzzle (康托展开+bfs)

Seven Puzzle

Time Limit : 1 sec, Memory Limit : 65536 KB

7パズルは8つの正方形のカードとこれらのカードがぴたりと収まる枠を使って行います。それぞれのカードは互いに区別できるように、0,1,2....7と番号がつけられています。枠には、縦に2個、横に4個のカードを並べることができます。

7パズルを始めるときには、まず枠にすべてのカードを入れます。枠のなかで0のカードだけは、上下左右に隣接するカードと位置を交換することができます。たとえば、枠の状態が図( a )のときに、0のカードの右に隣接した、7のカードと位置を交換すれば、図( b )の状態になります。あるいは、図( a )の状態から0のカードの下に隣接した2のカードと位置を交換すれば図( c )の状態になります。カードの位置を入れ替える操作はこれだけが許されます。図( a )の状態で0のカードと上下左右に隣接するカードは7と2のカードだけなので、これ以外の位置の入れ替えは許されません。

ゲームの目的は、カードをきれいに整列して図( d )の状態にすることです。最初の状態を入力とし、カードをきれいに整列するまでに、必要な最小手数を出力して終了するプログラムを作成してください。ただし、入力されたカードの状態からは図( d )の状態に移ることは可能であるとします。

入力データは、1行に8つの数字が与えられます。これらは、最初の状態のカードの並びを表します。図( a )の数字表現は0,7,3,4,2,5,1,6に、図( c )は2,7,3,4,0,5,1,6となります。

図( a ) 0,7,3,4,2,5,1,6の場合 図( b ) 7,0,3,4,2,5,1,6の場合
図( c ) 2,7,3,4,0,5,1,6の場合 図( d ) 0,1,2,3,4,5,6,7(最終状態)

Input

1つ目のパズルの状態(整数;空白区切り)
2つ目のパズルの状態(整数;空白区切り)
     :
     :

与えられるパズルの数は1000以下です。

Output

1つ目のパズルの状態から最終状態へ移行する最小手数(整数)
2つ目のパズルの状態から最終状態へ移行する最小手数(整数)
            :
            :

Sample Input

0 1 2 3 4 5 6 7
1 0 2 3 4 5 6 7
7 6 5 4 3 2 1 0

Output for the Sample Input

0
1
28

题意:给个初始状态为0-7的一个排列,0表示空格。可以挪动空格周围的数字,最后使得最终状态为0-7顺序排列。

分析:就是类似八数码的问题。之前没做过八数码的题。

记录下思考过程。首先很容易想到就是从初始状态开始搜索出最终状态,每次的策略就是移动0周围的数字。但,怎么表示状态呢?这8个格子不是可以看成矩阵用二维数组记录吗?于是。。。

1、可以用一个结构体,结构体中有一个二维数组来记录,另一个变量记录初始状态搜到的步数2、结构体中的数组可以用一维数组,这样策略就是0所在的位置p与p+1、p-1、p+4、p-4四个位置的元素交换,当然交换的pos位置必须在0-7范围内,且   换行的地方要注意!!!p=3时,pos=4是不行的p=4时,pos=3是不行的

然后就是bfs搜了。状态有8!(8个数全排列)= 40320

开始用的sum(a[i]*i)来hash,总是从7 6 5 4 3 2 1 0搜不到目标状态。后来意识到是hash的时候有重复造成的错误!

于是看了下康托展开:http://zh.wikipedia.org/wiki/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80康托展开是一个全排列到一个自然数双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

于是改写了hash方法后,能过样例了。。。但是tle于是把之前所有用vector的地方都改成了手写数组int a[8]。。还是tle

于是想到预处理,从0 1 2 3 4 5 6 7这个排列作初始状态出发来搜,hash[i]记录到达i对应的排列的步数n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。所以hash数组只要开8!+1=40321即可!OK!

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cstdlib>
  6 #include<cmath>
  7 #include<vector>
  8 #include<queue>
  9 #include<map>
 10 #include<set>
 11
 12 using namespace std;
 13
 14 typedef long long ll;
 15
 16 struct node
 17 {
 18     int sta[8];
 19     int step;
 20     int p;
 21 };
 22 queue<node> q;
 23 int s[8];
 24
 25 int d[] = {-4,-1,1,4};
 26 int hash[40321];
 27 int w[10],vec[10];
 28
 29 void init()
 30 {
 31     w[0] = 1;
 32     int sum = 1;
 33     for(int i=1;i<=7;i++)
 34     {
 35         sum*=i;
 36         w[i] = sum;
 37     }
 38 }
 39
 40 void solve()
 41 {
 42     while(!q.empty()) q.pop();
 43     memset(hash,-1,sizeof(hash));
 44
 45     node ff,gg,hh;
 46     for(int i=0;i<8;i++)
 47         ff.sta[i] = i;
 48     ff.step = 0;
 49
 50
 51     int mark = 0,pos;
 52     int n = 8;
 53     for(int i=0;i<n;i++)
 54     {
 55         int cnt = 0;
 56         for(int j=i+1;j<n;j++)
 57         {
 58             if(s[j]<s[i]) cnt++;
 59         }
 60         vec[n-i-1] = cnt;
 61     }
 62     for(int i=0;i<n;i++)
 63         mark += vec[i]*w[i];
 64     mark++;
 65     ff.p = mark;
 66     q.push(ff);
 67     hash[mark] = 0;
 68
 69     int cur[8],next[8];
 70     while(!q.empty())
 71     {
 72         gg = q.front();
 73         q.pop();
 74         memcpy(cur,gg.sta,sizeof(gg.sta));
 75         int step = gg.step;
 76
 77         for(int i=0;i<8;i++)
 78         {
 79             if(cur[i] == 0)
 80             {
 81                 pos = i;
 82                 break;
 83             }
 84         }
 85
 86         for(int i=0;i<4;i++)
 87         {
 88             int x = pos+d[i];
 89             if(x<0 || x>7) continue;
 90             if(x==3 && pos==4) continue;
 91             if(x==4 && pos==3) continue;
 92
 93             memcpy(next,cur,sizeof(cur));
 94
 95             int tmp = next[x];
 96             next[x] = next[pos];
 97             next[pos] = tmp;
 98
 99             int mark = 0;
100             for(int k=0;k<8;k++)
101             {
102                 int cnt = 0;
103                 for(int j=k+1;j<8;j++)
104                 {
105                     if(next[j]<next[k]) cnt++;
106                 }
107                 vec[n-k-1] = cnt;
108             }
109             for(int k=0;k<8;k++)
110                 mark += vec[k]*w[k];
111             mark++;
112             if(hash[mark]!=-1) continue;
113
114             memcpy(hh.sta,next,sizeof(next));
115             hh.step = step+1;
116             hh.p = mark;
117             q.push(hh);
118             hash[mark] = hh.step;
119         }
120     }
121 }
122
123 int main()
124 {
125    // freopen("1.txt","w",stdout);
126
127     init();
128     solve();
129     int x;
130     while(~scanf("%d",&s[0]))
131     {
132         for(int i=1;i<=7;i++)
133         {
134             scanf("%d",&x);
135             s[i] = x;
136         }
137         int omg = 0;
138         for(int i=0;i<8;i++)
139         {
140             int cnt = 0;
141             for(int j=i+1;j<8;j++)
142             {
143                 if(s[j]<s[i])
144                     cnt++;
145             }
146             vec[8-1-i] = cnt;
147         }
148         for(int i=0;i<8;i++)
149             omg += vec[i]*w[i];
150         omg++;
151         printf("%d\n",hash[omg]);
152     }
153     return 0;
154 }

				
时间: 2024-10-27 05:52:04

Aizu 0121 Seven Puzzle (康托展开+bfs)的相关文章

POJ 1077 Eight(康托展开+BFS)

Eight Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 30176   Accepted: 13119   Special Judge Description The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15

HDU 3567 Eight II 打表,康托展开,bfs,g++提交可过c++不可过 难度:3

http://acm.hdu.edu.cn/showproblem.php?pid=3567 相比Eight,似乎只是把目标状态由确定的改成不确定的,但是康托展开+曼哈顿为h值的A*和IDA*都不过,而且也不好控制字典序 换个角度想,虽然起始状态有很多,但是到底哪一位是1,哪一位是2不是最重要的,最重要的是和目标状态对应,所以可以把起始状态重新编码为"12345678"这种形式(先不考虑X),然后目标状态也对应过去,当考虑X的时候,我们可以认为起始状态只有9种,分别是'X'在各个位置的

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

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

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

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

Aizu 0121 Seven Puzzle

一遍预处理跑完所有情况,O(1)回答就好.状态记录我用的康拓和逆康拓. #include<bits/stdc++.h> using namespace std; int d[40320]; int fac[8]; int u[8]; int cantor() { int re = 0; for(int i = 0; i < 8; i++){ int inv = 0; for(int j = i; ++j < 8; ){ if(u[i]>u[j]) inv++; } re +=

AOJ 0121 Seven Puzzle(反向BFS+map)

卡了很久的一道题,之前用正向BFS硬缸,结果凉凉,下面是之前错误的代码,仅供自己记录,他人浏览请直接跳过: 1 #include<cstring> 2 #include<iostream> 3 #include<queue> 4 #include<algorithm> 5 #include<cstdio> 6 7 using namespace std; 8 9 int tmaze[2][4]; 10 struct node{ 11 int ma

HDU 1043 Eight (BFS&#183;八数码&#183;康托展开)

题意  输出八数码问题从给定状态到12345678x的路径 用康托展开将排列对应为整数  即这个排列在所有排列中的字典序  然后就是基础的BFS了 #include <bits/stdc++.h> using namespace std; const int N = 5e5, M = 9; int x[4] = { -1, 1, 0, 0}; int y[4] = {0, 0, -1, 1}; int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320

POJ 1077 Eight(bfs+康托展开)

Eight Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 41040   Accepted: 16901   Special Judge Description The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15

HDU 1043 Eight(反向BFS+打表+康托展开)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 题目大意:传统八数码问题 解题思路:就是从“12345678x”这个终点状态开始反向BFS,将各个状态记录下来,因为数字太大所以用康托展开将数字离散化. 代码: 1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<st