题意就不用再说明了吧......如此经典
之前想用双向广搜、a*来写,但总觉得无力,现在用IDA*感觉其他的解法都弱爆了..............想法活跃,时间,空间消耗很小,给它跪了
启发式搜索关键还是找估价函数:此题估价函数可大致定性为每个数字(除去x,只要8个数字)当前位置与它期望位置的曼哈顿距离
即为:v += abs(i - pos[map[i][j] - 1][0]); v += abs(j - pos[map[i][j] - 1][1]); 大致估算为几十步内得出结果。
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <vector> #include <set> #include <queue> #include <stack> #include <climits>//形如INT_MAX一类的 #define MAX 100005 #define INF 0x7FFFFFFF #define REP(i,s,t) for(int i=(s);i<=(t);++i) #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define mp(a,b) make_pair(a,b) #define L(x) x<<1 #define R(x) x<<1|1 # define eps 1e-5 //#pragma comment(linker, "/STACK:36777216") ///传说中的外挂 using namespace std; int pos[9][2] = { //各个数字的初始位置 {0,0},{0,1},{0,2}, {1,0},{1,1},{1,2}, {2,0},{2,1},{2,2} }; int map[4][4]; int buff[50]; char input[11]; int limit,ok; int dx[] = {-1,0,1,0}; //u r d l____0 1 2 3 int dy[] = {0,1,0,-1}; char op[] = {'u','r','d','l'}; int h(int x,int y) { int v = 0; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { if(i != x || j != y) { v += abs(i - pos[map[i][j] - 1][0]); v += abs(j - pos[map[i][j] - 1][1]); } } } return v; } int dfs(int x,int y,int step,int pre) { int hn = h(x,y); if(hn == 0) { ok = 1; return step; } if(hn + step > limit) return hn + step; int minn = INF; for(int i=0; i<4; i++) { if(abs(i - pre) == 2) continue; int xx = x + dx[i]; int yy = y + dy[i]; if(xx<0 || xx >=3 || yy<0 || yy >=3) continue; buff[step] = i; swap(map[x][y],map[xx][yy]); int tmp = dfs(xx,yy,step+1,i); if(ok) return tmp; minn = min(tmp,minn); swap(map[x][y],map[xx][yy]); } return minn; } void IDA_star(int x,int y) { ok = 0; memset(buff,-1,sizeof(buff)); while(ok == 0 && limit <= 36) { limit = dfs(x,y,0,-111); } if(ok == 1) { for(int i=0; i<limit; i++) printf("%c",op[buff[i]]); puts(""); } else puts("unsolvable"); } int main() { for(int i=0; i<9; i++) cin >> input[i]; int t = 0,x,y; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { if(input[t] == 'x') { map[i][j] = 9; x = i; y = j; t++; } else map[i][j] = input[t++] - '0'; } } int limit = h(x,y); if(limit == 0) { puts(""); return 0; } IDA_star(x,y); return 0; }
想吐槽一下杭电的这题.........估计各种无法到达目标的数据,所以在输入时候通过求逆序数对数来判断是否有解,本来是TLE,一下蹦到171ms
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <vector> #include <set> #include <queue> #include <stack> #include <climits>//形如INT_MAX一类的 #define MAX 100005 #define INF 0x7FFFFFFF #define REP(i,s,t) for(int i=(s);i<=(t);++i) #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define mp(a,b) make_pair(a,b) #define L(x) x<<1 #define R(x) x<<1|1 # define eps 1e-5 //#pragma comment(linker, "/STACK:36777216") ///传说中的外挂 using namespace std; int pos[9][2] = { //各个数字的初始位置 {0,0},{0,1},{0,2}, {1,0},{1,1},{1,2}, {2,0},{2,1},{2,2} }; int map[4][4]; int buff[50]; char input[11]; int limit,ok; int dx[] = {-1,0,1,0}; //u r d l____0 1 2 3 int dy[] = {0,1,0,-1}; char op[] = {'u','r','d','l'}; int h(int x,int y) { int v = 0; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { if(i != x || j != y) { v += abs(i - pos[map[i][j] - 1][0]); v += abs(j - pos[map[i][j] - 1][1]); } } } return v; } int dfs(int x,int y,int step,int pre) { int hn = h(x,y); if(hn == 0) { ok = 1; return step; } if(hn + step > limit) return hn + step; int minn = INF; for(int i=0; i<4; i++) { if(abs(i - pre) == 2) continue; int xx = x + dx[i]; int yy = y + dy[i]; if(xx<0 || xx >=3 || yy<0 || yy >=3) continue; buff[step] = i; swap(map[x][y],map[xx][yy]); int tmp = dfs(xx,yy,step+1,i); if(ok) return tmp; minn = min(tmp,minn); swap(map[x][y],map[xx][yy]); } return minn; } int canget(int a[4][4]) { //这一步省了好多时间 求逆序数对数 int i,j,sum=0,b[10],k=0; for(i=0; i<3; i++) { for(j=0; j<3; j++) { if(a[i][j] != 9) b[++k]=a[i][j]; } } for(i=1; i<=8; i++) { for(j=1; j<i; j++) { if(b[i]<b[j])sum++; } } if(sum % 2 ==0)return 1; else return 0; } void IDA_star(int x,int y) { ok = 0; memset(buff,-1,sizeof(buff)); while(ok == 0 && limit <= 30) { limit = dfs(x,y,0,-111); } if(ok == 1) { for(int i=0; i<limit; i++) printf("%c",op[buff[i]]); puts(""); } else puts("unsolvable"); } int main() { while(cin >> input[0]) { //memset(map,0,sizeof(map)); for(int i=1; i<9; i++) cin >> input[i]; int t = 0,x,y; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { if(input[t] == 'x') { map[i][j] = 9; x = i; y = j; t++; } else map[i][j] = input[t++] - '0'; } } limit = h(x,y); if(limit == 0) { puts(""); continue; } if(canget(map)) IDA_star(x,y); else puts("unsolvable"); } return 0; }
经过猥琐的测试,limit最小限制在29步....................