双向广搜+hash+康托展开 codevs 1225 八数码难题

Posted tech-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向广搜+hash+康托展开 codevs 1225 八数码难题相关的知识,希望对你有一定的参考价值。

codevs 1225 八数码难题

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
 
题目描述 Description

Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.
问题描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入描述 Input Description

输入初试状态,一行九个数字,空格用0表示

输出描述 Output Description

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

样例输入 Sample Input

283104765

样例输出 Sample Output

4

数据范围及提示 Data Size & Hint

详见试题

分类标签 Tags 点此展开 

启发式搜索 广度优先搜索 深度优先搜索 迭代搜索 搜索

  1 /*复杂题目模板加注解*/
  2 # include <stdio.h>
  3 # include <mem.h>
  4 
  5 # define MAXN (362880 + 5)
  6 
  7 typedef struct 
  8 {
  9     char a[9];
 10 }state;
 11 
 12 const int dir[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}};
 13 int fact[9];
 14 
 15 int front, rear;
 16 state cur, nst;            /* new state */
 17 char vis[MAXN];
 18 char dist[MAXN];        /* 求的是最短距离( < 100),可以用 char 类型 */
 19 state Q[MAXN/2];
 20 
 21 
 22 void read(state *s);
 23 int inversions(state s);
 24 int cantor(state s);
 25 void init_fact(void);
 26 int bfs_d(state start, state goal);
 27 
 28 int main()
 29 {
 30     state start, goal;
 31     
 32     freopen("in.txt", "r", stdin);
 33     freopen("out.txt", "w", stdout);
 34     
 35     init_fact();
 36 
 37     read(&start);/*指针引用*/
 38     read(&goal);
 39 
 40     if (inversions(start)%2 == inversions(goal)%2)/
 41     {/*判断能不能到达最终状态,如果能达到最终状态的话,那么格子中的个数在后面有几个比他小的数的和的奇偶是不变的,无论怎么变换,可以用归纳推理证明*/
 42         printf("%d\n", bfs_d(start, goal));
 43     }
 44     else puts("-1");/*找不到最终的状态*/
 45 
 46     return 0;
 47 }
 48 
 49 int bfs_d(state start, state goal)
 50 {
 51     int i, x, y, nx, ny, ct, nt;
 52 
 53     memset(vis, 0, sizeof(vis));
 54     memset(dist, 0, sizeof(dist));
 55 
 56     front = 1;
 57     Q[front] = start;
 58     rear = 2;
 59     Q[rear++] = goal;
 60     vis[cantor(start)] = 1;                /* 1 表示从起始节点扩展得到 */
 61     vis[cantor(goal)] = 2;                /* 2 表示从目标节点扩展得到 */
 62 
 63     while (front < rear)
 64     {
 65         cur = Q[front++];
 66         ct = cantor(cur);
 67         for (i = 0; cur.a[i] && i < 9; ++i);/*找出0的位置*/
 68         x = i / 3;/*求出0所在的行数列数*/
 69         y = i % 3;
 70         for (i = 0; i < 4; ++i)
 71         {
 72             nx = x + dir[i][0];/*把0向四周扩展*/
 73             ny = y + dir[i][1];
 74             if (nx>=0 && nx<3 && ny>=0 && ny<3)
 75             {
 76                 nst = cur;
 77                 nst.a[x*3+y] = cur.a[nx*3+ny];/*互换0的位置与对应元素的位置*/
 78                 nst.a[nx*3+ny] = 0;
 79                 if (!vis[nt = cantor(nst)])/*判断当前这个状态是否已经到过*/
 80                 {
 81                     Q[rear++] = nst;
 82                     /* foot[nt] = ct; */
 83                     dist[nt] = dist[ct] + 1;
 84                     vis[nt] = vis[ct];/*转移扩展的方向*/
 85                 }
 86                 else if (vis[ct] != vis[nt])/*如果已经到过,就是两者变换的次数和加上最后一次变化*/
 87                 {/*这是双向广搜的精髓,判断两个点是从不同的方向转移来的*/
 88                     return 1 + dist[nt] + dist[ct];
 89                 }
 90             }
 91         }
 92     }
 93 
 94     return -1;
 95 }
 96 
 97 void read(state *s)
 98 {
 99     int i;
100     char c[5];
101 
102     for (i = 0; i < 9; ++i)
103     {
104         scanf("%s", c);
105         if (c[0] == x) (*s).a[i] = 0;
106         else (*s).a[i] = c[0] - 0;
107     }
108 }
109 
110 int inversions(state s)
111 {
112     char ch;
113     int i, j, ret;
114 
115     ret = 0;
116     for (i = 0; i < 9; ++i)
117     {
118         if (s.a[i] == 0) continue;
119         ch = s.a[i];
120         for (j = i+1; j < 9; ++j)
121         {
122             if (s.a[j] < ch && s.a[j] != 0)
123                 ++ret;
124         }
125     }
126 
127     return ret;
128 }
129 
130 int cantor(state s)/*康托展开应用于哈希表,处理排列问题是不会有冲突的,网上有证明,可以自己看*/
131 {
132     char ch;
133     int i, j, ret, cnt;
134 
135     ret = 0;
136     for (i = 0; i < 9; ++i)
137     {
138         cnt = 0;
139         ch = s.a[i];
140         for (j = i+1; j < 9; ++j)
141         {
142             if (s.a[j] < ch)
143                 ++cnt;
144         }
145         ret += cnt*fact[8-i];
146     }
147 
148     return ret;
149 }
150 
151 void init_fact(void)
152 {
153     int i;
154 
155     fact[0] = 1;
156     for (i = 1; i < 9; ++i)
157     {
158         fact[i] = i * fact[i-1];/*处理阶乘,整张图一共有9!种状态*/
159     }
160 }

 

 

本题题解:

 1 #define N 3628800
 2 #include<iostream>
 3 #include<queue>
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 struct state{
 8     char a[10];
 9 }; 
10 queue<state>que;
11 int xx[]={0,0,1,-1};
12 int yy[]={1,-1,0,0};
13 int visit[N]={0};
14 int fact[10]={0};
15 int dis[N]={0};
16 void init_goal(state& goal)
17 {
18     goal.a[0]=1;goal.a[1]=2;goal.a[2]=3;
19     goal.a[3]=8;goal.a[4]=0;goal.a[5]=4;
20     goal.a[6]=7;goal.a[7]=6;goal.a[8]=5;
21     //goal.a="123804765";
22     //strcpy(goal.a,"123804765");
23 }
24 void in_fact()
25 {
26     fact[0]=1;
27     for(int i=1;i<9;++i)
28       fact[i]=i*fact[i-1];
29 }
30 int cantor(state k)
31 {
32     int ret=0;
33     for(int i=0;i<9;++i)
34     {
35         int cnt=0;
36         char ch=k.a[i];
37         for(int j=i+1;j<9;++j)
38           if(ch>k.a[j]) cnt++;
39         ret+=cnt*fact[8-i];
40     }
41     return ret;
42 }
43 int bfs(state begin,state goal)
44 {
45     que.push(begin);que.push(goal);
46     int k1=cantor(begin);
47     int k2=cantor(goal);
48     visit[k1]=1;
49     visit[k2]=2;
50     dis[k1]=0;dis[k2]=0;
51     while(!que.empty())
52     {
53         state fro=que.front();
54         int can_fro=cantor(fro);
55         que.pop();
56         int i;
57         for(i=0;fro.a[i]!=0&&i<9;++i);
58         int x=i/3;
59         int y=i%3;
60         for(int j=0;j<4;++j)
61         {
62             int nx=x+xx[j],ny=y+yy[j];
63             if(nx>=0&&nx<3&&ny>=0&&ny<3)
64             {
65                 state now=fro;
66                 now.a[3*x+y]=fro.a[3*nx+ny];
67                 now.a[3*nx+ny]=0;
68                 int kj=cantor(now);
69                 if(!visit[kj])
70                 {
71                     que.push(now);
72                     visit[kj]=visit[can_fro];/*转移是正向扩展还是逆向扩展*/
73                     dis[kj]=dis[can_fro]+1;
74                 }
75                 else if(visit[kj]!=visit[can_fro])
76                 {/*这是双向广搜的精髓,必须用visit表示当前这个是同时由正反广搜扩展来的*/
77                     return 1+dis[kj]+dis[can_fro];
78                 }
79             }
80         }
81     }
82 }
83 int main()
84 {
85     state goal,begin;
86     init_goal(goal);
87     in_fact();
88 //    scanf("%s",goal.a+1);
89     scanf("%s",begin.a);
90     printf("%d\n",bfs(begin,goal));
91     return 0;
92 }

 

以上是关于双向广搜+hash+康托展开 codevs 1225 八数码难题的主要内容,如果未能解决你的问题,请参考以下文章

双向广搜 codevs 3060 抓住那头奶牛

HDU 1043 Eight(双向BFS+康托展开)

康托展开:对全排列的HASH和还原,判断搜索中的某个排列是否出现过

康托展开 & 逆康托展开

康托展开

备忘录