POJ 1077 Eight

题目链接:http://poj.org/problem?id=1077

经典八数码问题,作为A*与IDA*的入门题来说是很不错的。

第一次学习A*与IDA*,代码参考了:http://www.cnblogs.com/liyongmou/archive/2010/07/19/1780861.html

推荐一篇介绍A*算法的文章:http://hi.baidu.com/catro/item/4782da1769edbd721109b5e9

英文原文:http://www.policyalmanac.org/games/aStarTutorial.htm

A*算法的动画演示:http://www.java3z.com/cwbwebhome/article/article2/2825.html

推荐一篇介绍IDA*的文章:http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756

hash函数用的康托展开和逆康托展开

启发函数:初始状态到当前状态所走的步数+当前状态到目标状态所有数字的曼哈顿距离之和

传统的BFS:

11510849 gbr 1077 Accepted 2636K 563MS C++ 4196B 2013-04-22 21:53:53
View Code
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <queue>
  5 
  6 using namespace std;
  7 
  8 char str[110];
  9 const int MAXN = 400000;
 10 const int fact[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
 11 const int dx[] = { -1, 1, 0, 0 };
 12 const int dy[] = { 0, 0, -1, 1 };
 13 
 14 struct node
 15 {
 16     int s[10];
 17     int addr;  //空格的位置
 18 };
 19 
 20 bool vis[MAXN];
 21 int fa[MAXN];
 22 char step[MAXN];
 23 
 24 int GetNum( int *s )  //康托展开
 25 {
 26     int ans = 0;
 27     for ( int i = 0; i < 9; ++i )
 28     {
 29         int cnt = 0;
 30         for ( int j = i + 1; j < 9; ++j )
 31             if ( s[j] < s[i] ) ++cnt;
 32 
 33         ans += fact[ 8 - i ] * cnt;
 34     }
 35     return ans;
 36 }
 37 
 38 void GetNode( node &D, int num )   //逆康托展开
 39 {
 40     bool use[10];
 41     memset( use, false, sizeof(use) );
 42 
 43     for ( int i = 0; i < 9; ++i )
 44     {
 45         int t = num / fact[ 8 - i ];
 46         num %= fact[ 8 - i ];
 47         int j, cnt;
 48         for ( j = 1, cnt = 0; cnt <= t; ++j )
 49             if ( !use[j] ) ++cnt;
 50         use[j - 1] = true;
 51         D.s[i] = j - 1;
 52         if ( j - 1 == 9 ) D.addr = i;
 53     }
 54     return;
 55 }
 56 
 57 void Init( node &st )   //得到初始状态
 58 {
 59     int cnt = 0;
 60     for ( int i = 0; str[i]; ++i )
 61     {
 62         if ( str[i] != ' ' )
 63         {
 64             if ( str[i] != 'x' )
 65                 st.s[cnt++] = str[i] - '0';
 66             else
 67             {
 68                 //printf("cnt=%d\n", cnt);
 69                 st.s[cnt++] = 9;
 70                 st.addr = cnt - 1;
 71             }
 72         }
 73     }
 74 
 75     return;
 76 }
 77 
 78 bool check( int x, int y ) //检验坐标是否合法
 79 {
 80     return x >= 0 && x < 3 && y >= 0 && y < 3;
 81 }
 82 
 83 void BFS( node start )
 84 {
 85     //初始化
 86     memset( vis, false, sizeof(vis) );
 87     int hash = GetNum( start.s );
 88     vis[ hash ] = true;
 89     fa[ hash ] = -1;
 90 
 91     queue<int> Q;
 92 
 93     Q.push( hash );    //初始状态入队
 94     while ( !Q.empty() )
 95     {
 96         int u = Q.front();
 97         Q.pop();
 98 
 99         node cur;
100         GetNode( cur, u );   //得到队首节点的状态
101         int x = cur.addr / 3;   //得到空格的位置
102         int y = cur.addr % 3;
103 
104         for ( int i = 0; i < 4; ++i )    //四方向移动
105         {
106             int xx = x + dx[i];   //新的空格的位置
107             int yy = y + dy[i];
108             if ( check( xx, yy ) )   //如果坐标合法
109             {
110                 node temp = cur;
111                 temp.addr = xx * 3 + yy;    //新的空格的位置
112 
113                 //移动空格后,得到新的状态
114                 int swp = temp.s[ temp.addr ];
115                 temp.s[ temp.addr ] = temp.s[ cur.addr ];
116                 temp.s[ cur.addr ] = swp;
117 
118                 hash = GetNum( temp.s );
119                 if ( !vis[ hash ] )   //新状态没被访问过
120                 {
121                     vis[hash] = true;
122                     step[hash] = i;
123                     fa[hash] = u;
124                     if ( hash == 0 ) return;
125                     Q.push( hash );
126                 }
127             }
128         }
129     }
130 }
131 
132 void PrintPath( int cur )
133 {
134     if ( fa[cur] == -1 ) return;
135     PrintPath( fa[cur] );
136     switch( step[cur] )
137     {
138     case 0:
139         putchar('u');
140         break;
141     case 1:
142         putchar('d');
143         break;
144     case 2:
145         putchar('l');
146         break;
147     case 3:
148         putchar('r');
149         break;
150     }
151 }
152 
153 int main()
154 {
155     while ( gets(str) != NULL )
156     {
157         node st;
158         Init( st );
159         BFS( st );
160         if ( vis[0] )
161         {
162             PrintPath( 0 );
163             puts("");
164         }
165         else puts("unsolvable");
166     }
167     return 0;
168 }

A*

之前把启发函数写错了,导致跑了600MS……改了之后只有16MS……A*果然很强大……

11511544 gbr 1077 Accepted 5080K 16MS C++ 4785B 2013-04-23 00:15:35
11511459 gbr 1077 Accepted 5444K 641MS C++ 4777B 2013-04-22 23:45:25
View Code
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <queue>
  5 
  6 using namespace std;
  7 
  8 char str[110];
  9 const int MAXN = 400000;
 10 const int fact[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
 11 const int dx[] = { -1, 1, 0, 0 };
 12 const int dy[] = { 0, 0, -1, 1 };
 13 const int goal[9][2] =
 14 {
 15     {0, 0}, {0, 1}, {0, 2},
 16     {1, 0}, {1, 1}, {1, 2},
 17     {2, 0}, {2, 1}, {2, 2}
 18 };
 19 
 20 struct node
 21 {
 22     int s[10];
 23     int addr;          //空格的位置
 24 };
 25 
 26 int fa[MAXN];          //保存父节点
 27 int f[MAXN], d[MAXN];  //f记录已经计算过的代价, d记录起始状态到该状态所需代价
 28 char step[MAXN];       //保存路径
 29 char color[MAXN];      //标记节点, 0=未访问,1=在open表中,2=在close表中
 30 
 31 struct cmp
 32 {
 33     bool operator()( int u, int v )
 34     {
 35         return f[u] > f[v];
 36     }
 37 };
 38 
 39 int GetNum( int *s )  //康托展开
 40 {
 41     int ans = 0;
 42     for ( int i = 0; i < 9; ++i )
 43     {
 44         int cnt = 0;
 45         for ( int j = i + 1; j < 9; ++j )
 46             if ( s[j] < s[i] ) ++cnt;
 47 
 48         ans += fact[ 8 - i ] * cnt;
 49     }
 50     return ans;
 51 }
 52 
 53 void GetNode( node &D, int num )   //逆康托展开
 54 {
 55     bool use[10];
 56     memset( use, false, sizeof(use) );
 57 
 58     for ( int i = 0; i < 9; ++i )
 59     {
 60         int t = num / fact[ 8 - i ];
 61         num %= fact[ 8 - i ];
 62         int j, cnt;
 63         for ( j = 1, cnt = 0; cnt <= t; ++j )
 64             if ( !use[j] ) ++cnt;
 65         use[j - 1] = true;
 66         D.s[i] = j - 1;
 67         if ( j - 1 == 9 ) D.addr = i;
 68     }
 69     return;
 70 }
 71 
 72 void Init( node &st )   //得到初始状态
 73 {
 74     int cnt = 0;
 75     for ( int i = 0; str[i]; ++i )
 76     {
 77         if ( str[i] != ' ' )
 78         {
 79             if ( str[i] != 'x' )
 80                 st.s[cnt++] = str[i] - '0';
 81             else
 82             {
 83                 //printf("cnt=%d\n", cnt);
 84                 st.s[cnt++] = 9;
 85                 st.addr = cnt - 1;
 86             }
 87         }
 88     }
 89     return;
 90 }
 91 
 92 bool check( int x, int y ) //检验坐标是否合法
 93 {
 94     return x >= 0 && x < 3 && y >= 0 && y < 3;
 95 }
 96 
 97 int h( int *s )   //启发函数:所有数字到该数字目标状态的曼哈顿距离之和
 98 {
 99     int hv = 0;
100     for ( int i = 0; i < 9; ++i )
101     {
102         int u = s[i];
103         int x = i / 3;
104         int y = i % 3;
105         if ( u != 9 )
106         {
107             hv += abs( x - goal[u - 1][0] ) + abs( y - goal[u - 1][1] );
108         }
109     }
110     return hv;
111 }
112 
113 void A_Star( node start )
114 {
115     priority_queue<int, vector<int>, cmp> open;   //优先队列实现open表
116     memset( color, 0, sizeof(color) );
117 
118     int hash = GetNum( start.s );
119     fa[hash] = -1;
120     d[hash] = 0;
121     f[hash] = h( start.s );
122     open.push( hash );                            //头结点加入open表
123     color[hash] = 1;
124 
125     while ( !open.empty() )
126     {
127         int u = open.top();
128         if ( u == 0 ) return;                     //得到目标状态,搜索结束
129         open.pop();
130 
131         node cur;
132         GetNode( cur, u );                        //得到当前状态与空格位置
133         int x = cur.addr / 3;
134         int y = cur.addr % 3;
135 
136         for ( int i = 0; i < 4; ++i )             //四方向移动
137         {
138             int xx = x + dx[i];
139             int yy = y + dy[i];
140             if ( check( xx, yy ) )
141             {
142                 node tmp = cur;
143                 tmp.addr = xx * 3 + yy;
144                                                   //得到新的状态
145                 int swp = tmp.s[tmp.addr];
146                 tmp.s[tmp.addr] = tmp.s[cur.addr];
147                 tmp.s[cur.addr] = swp;
148 
149                 hash = GetNum( tmp.s );
150                 //如果当前节点在open表中,表示当前状态已经访问过,
151                 //并且以现在这种方式访问到该状态比之前访问到该状态的方法更优秀,因此替换
152                 if ( color[hash] == 1 && ( d[u] + 1 ) < d[hash] )
153                 {
154                     step[hash] = i;
155                     f[hash] = f[hash] - d[hash] + d[u] + 1;
156                     d[hash] = d[u] + 1;
157                     fa[hash] = u;
158                     open.push(hash);
159                 }
160                 //检查close表,如果方法更优秀,则替换,重新将该状态加入open表
161                 else if ( color[hash] == 2 && d[u] + 1 < d[hash] )
162                 {
163                     step[hash] = i;
164                     f[hash] = f[hash] - d[hash] + d[u] + 1;
165                     d[hash] = d[u] + 1;
166                     fa[hash] = u;
167                     open.push(hash);
168                     color[hash] = 1;
169                 }
170                 else if ( color[hash] == 0 )   //如果之前该状态没被访问过,则计算f的值,将其加入open表
171                 {
172                     step[hash] = i;
173                     d[hash] = d[u] + 1;
174                     f[hash] = d[hash] + h(tmp.s);
175                     fa[hash] = u;
176                     open.push(hash);
177                     color[hash] = 1;
178                 }
179             }
180         }
181         color[u] = 2;  //该节点已被访问完毕,加入close表
182     }
183     return;
184 }
185 
186 void PrintPath( int cur )
187 {
188     if ( fa[cur] == -1 ) return;
189     PrintPath( fa[cur] );
190     switch( step[cur] )
191     {
192     case 0:
193         putchar('u');
194         break;
195     case 1:
196         putchar('d');
197         break;
198     case 2:
199         putchar('l');
200         break;
201     case 3:
202         putchar('r');
203         break;
204     }
205 }
206 
207 int main()
208 {
209     while ( gets(str) != NULL )
210     {
211         node st;
212         Init( st );
213 
214         A_Star( st );
215         if ( color[0] != 0 )
216         {
217             PrintPath( 0 );
218             puts("");
219         }
220         else puts("unsolvable");
221     }
222     return 0;
223 }

IDA*

据说写好了应该是0MS,但是我的跑了16MS,不知道哪里写搓了……

11511603 gbr 1077 Accepted 160K 16MS C++ 3015B 2013-04-23 00:35:23
View Code
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 
  5 char str[110];
  6 const int MAXN = 1000;
  7 const int dx[] = { -1, 0, 0, 1 };
  8 const int dy[] = { 0, -1, 1, 0 };
  9 const char dic[] = "ulrd";
 10 const int goal[9][2] =
 11 {
 12     {0, 0}, {0, 1}, {0, 2},
 13     {1, 0}, {1, 1}, {1, 2},
 14     {2, 0}, {2, 1}, {2, 2}
 15 };
 16 
 17 int ans[MAXN];     //保存路径
 18 int inital[3][3];  //当前状态
 19 int limit;         //每次迭代加深的上界
 20 bool find;         //是否找到答案
 21 int stx, sty;      //开始时空格的位置
 22 
 23 int min( int a, int b )
 24 {
 25     return a < b ? a : b;
 26 }
 27 
 28 void Init( )   //得到初始状态
 29 {
 30     int cnt = 0;
 31     for ( int i = 0; str[i]; ++i )
 32     {
 33         if ( str[i] != ' ' )
 34         {
 35             if ( str[i] != 'x' )
 36             {
 37                 int x = cnt / 3;
 38                 int y = cnt % 3;
 39                 inital[x][y] = str[i] - '0';
 40             }
 41             else
 42             {
 43                 stx = cnt / 3;
 44                 sty = cnt % 3;
 45                 inital[stx][sty] = 9;
 46             }
 47             ++cnt;
 48         }
 49     }
 50     return;
 51 }
 52 
 53 bool check( int x, int y ) //检验坐标是否合法
 54 {
 55     return x >= 0 && x < 3 && y >= 0 && y < 3;
 56 }
 57 
 58 int h( int s[][3] )   //估价函数
 59 {
 60     int hv = 0;
 61     for ( int i = 0; i < 3; ++i )
 62         for ( int j = 0; j < 3; ++j )
 63         {
 64             int u = inital[i][j];
 65             if ( u != 9 )
 66             {
 67                 hv += abs( i - goal[u - 1][0] ) + abs( j - goal[u - 1][1] );
 68             }
 69         }
 70     return hv;
 71 }
 72 
 73 int DFS( int x, int y, int dv, int pre_step )
 74 {
 75     int hv = h(inital);      //该状态到目标状态的代价
 76     //printf("hv = %d\n", hv);
 77     if ( hv + dv > limit )   //若此状态函数值大于本次搜索的上界,则返回
 78         return hv + dv;
 79     if ( hv == 0 )           //到达目标状态,停止搜索
 80     {
 81         find = true;
 82         return dv;
 83     }
 84 
 85     int next_limit = 1e9;
 86     for ( int i = 0; i < 4; ++i )
 87     {
 88         if ( i + pre_step == 3 ) continue;   //如果本次移动与上次移动的方向相反,跳过
 89 
 90         int xx = x + dx[i];
 91         int yy = y + dy[i];
 92         if ( check( xx, yy ) )
 93         {
 94             ans[dv] = i;                     //得到新的状态
 95             int swp = inital[x][y];
 96             inital[x][y] = inital[xx][yy];
 97             inital[xx][yy] = swp;
 98 
 99             int new_limit = DFS( xx, yy, dv + 1, i ); //由此向下搜索
100             if ( find )
101             {
102                 return new_limit;
103             }
104             next_limit = min( next_limit, new_limit );   //逐次加深上界
105 
106             swp = inital[x][y];                          //还原回之前的状态
107             inital[x][y] = inital[xx][yy];
108             inital[xx][yy] = swp;
109         }
110     }
111 
112     return next_limit;
113 }
114 
115 void IDA_Star( int x, int y )
116 {
117     find = false;
118     limit = h(inital);
119     while ( !find && limit <= 100 )
120         limit = DFS( x, y, 0, -10 );
121     return;
122 }
123 
124 int main()
125 {
126     while ( gets(str) != NULL )
127     {
128         Init();
129 
130         IDA_Star( stx, sty );
131         if ( find )
132         {
133             for ( int i = 0; i < limit; ++i )
134                 putchar( dic[ ans[i] ] );
135             puts("");
136         }
137         else puts("unsolvable");
138     }
139     return 0;
140 }
原文地址:https://www.cnblogs.com/GBRgbr/p/3037783.html