题意:5x5的棋盘有12只黑马,12只白马,一个空位,移动采用国际象棋马的移动方法,要求最少步数移动到目标局面,如果步数大于15步,输出-1.
分析:
这是昨天比赛的第一题,比赛时我用的双向BFS+hash判重,A了,不幸在BZOJ上TLE,按照lyd神犇的话说,步数大于15步输出-1这种限制就是告诉你要用ID-DFS,这道题状态有些多,我们还需要加个A*优化(gzz神犇比赛时就写出来了,无限orz中...)
双向BFS+hash判重代码(TLE):
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef int sta[30];
const int p = 1000007, dx[] = {-2,-1,1,2,2,1,-1,-2}, dy[] = {-1,-2,-2,-1,1,2,2,1};
char s[10];
int T,x,y,hd[p],nxt[p],hdd[p],nxtt[p],d[1000000],dd[1000000];
sta st[1000000],stt[1000000], goal = {1,1,1,1,1,0,1,1,1,1,0,0,2,1,1,0,0,0,0,1,0,0,0,0,0};
int hash(sta &a) {
int sum = 0;
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
sum = (sum*3+a[i*5+j])%p;
return sum;
}
bool in(int a) {
int h = hash(st[a]);
for(int i = hd[h]; i; i = nxt[i])
if(!memcmp(st[i], st[a], sizeof st[a])) return false;
nxt[a] = hd[h], hd[h] = a;
return true;
}
int srch(int a) {
int h = hash(stt[a]);
for(int i = hd[h]; i; i = nxt[i])
if(!memcmp(st[i], stt[a], sizeof stt[a])) return d[i];
return -1;
}
bool in2(int a) {
int h = hash(stt[a]);
for(int i = hdd[h]; i; i = nxtt[i])
if(!memcmp(stt[i], stt[a], sizeof stt[a])) return false;
nxtt[a] = hdd[h], hdd[h] = a;
return true;
}
int bfs() {
int front = 1, rear = 2;
while(front < rear) {
sta &u = st[front];
if(d[front] > 7) break;
if(!memcmp(goal, u, sizeof u)) return d[front];
for(int i = 0; i < 25; i++)
if(u[i] == 2) {
x = i/5, y = i%5;
break;
}
for(int i = 0; i < 8; i++) {
int xx = x+dx[i], yy = y+dy[i];
if(xx >= 0 && xx < 5 && yy >= 0 && yy < 5) {
sta &tmp = st[rear];
memcpy(&tmp, &u, sizeof u);
swap(tmp[x*5+y], tmp[xx*5+yy]);
if(in(rear)) d[rear] = d[front]+1, rear++;
}
}
front++;
}
return -1;
}
int bfs2() {
int front = 1, rear = 2;
memcpy(stt[1], goal, sizeof stt[1]);
while(front < rear) {
sta &u = stt[front];
if(dd[front] > 7) break;
int a = srch(front);
if(~a) return a+dd[front];
for(int i = 0; i < 25; i++)
if(u[i] == 2) {
x = i/5, y = i%5;
break;
}
for(int i = 0; i < 8; i++) {
int xx = x+dx[i], yy = y+dy[i];
if(xx >= 0 && xx < 5 && yy >= 0 && yy < 5) {
sta &tmp = stt[rear];
memcpy(&tmp, &u, sizeof u);
swap(tmp[x*5+y], tmp[xx*5+yy]);
if(in2(rear)) dd[rear] = dd[front]+1, rear++;
}
}
front++;
}
return -1;
}
int main() {
scanf("%d", &T);
while(T--) {
memset(hd, 0, sizeof hd);
memset(hdd, 0, sizeof hdd);
for(int i = 0; i < 5; i++) {
scanf("%s", s);
for(int j = 0; j < 5; j++) {
st[1][i*5+j] = s[j]-'0';
if(s[j] == '*') st[1][i*5+j] = 2;
}
}
int a = bfs();
if(~a) printf("%d\n", a);
else printf("%d\n", bfs2());
}
return 0;
}
ID-DFS+A*代码(AC):
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int dx[]={-2,-1,1,2,2,1,-1,-2}, dy[]={-1,-2,-2,-1,1,2,2,1};
char s[9];
int T,ok,x,y,k,a[5][5],goal[5][5]={{1,1,1,1,1},{0,1,1,1,1},{0,0,2,1,1},{0,0,0,0,1},{0,0,0,0,0}};
bool okk(int dep) {
int cnt = 0;
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
if(a[i][j] != goal[i][j]) {
cnt++;
if(cnt+dep > k) return false;
}
return true;
}
void dfs(int dep, int x, int y) {
if(dep == k && !memcmp(a,goal,sizeof a)) {ok = 1; return;}
for(int i = 0; i < 8; i++) if(!ok && dep < k) {
int xx = x+dx[i], yy = y+dy[i];
if(xx >= 0 && xx < 5 && yy >= 0 && yy < 5) {
swap(a[x][y], a[xx][yy]);
if(okk(dep)) dfs(dep+1, xx, yy);
swap(a[x][y], a[xx][yy]);
}
}
}
int main() {
scanf("%d", &T);
while(T--) {
for(int i = 0; i < 5; i++) {
scanf("%s", s);
for(int j = 0; j < 5; j++) if(s[j]=='*') x=i,y=j,a[i][j]=2; else a[i][j]=s[j]-'0';
}
ok = 0;
for(k = 1; k <= 15; k++) {
dfs(0, x, y);
if(ok) {printf("%d\n", k); break;}
}
if(!ok) puts("-1");
}
return 0;
}
由上还可以看出ID-DFS+A*不仅代码短,跑的还很快...