题目链接: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 |
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 |
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 |
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 }