soj1091 指环王 bfs+hash+剪枝

来源:互联网 发布:红会福娃娃 知乎 编辑:程序博客网 时间:2024/06/08 03:59
     

     原题链接http://acm.scu.edu.cn/soj/problem.action?id=1091

     这题的主要解法就是搜索,我用的是bfs,用map将二维数组处理成字符串作为主键,到达当前状态的最少步数作为键值,就能实现判重,如果当前最少步数已经超过10步,直接退出。但是这样做的时间是1292ms,虽然能够通过,但没能达到最优。

    如果能够找到完美的编码函数,就可以不用map,时间应该能够更快,但是25!/12!/12!已经超过了数组的最大容量,找到编码函数也没用了。后来我又想到可以利用剪枝,因为这题限制了10步,设当前的位置不对的棋子数为h(x),当前已经走了y步,如果h(x)+y>10就说明不可能在10步之内完成,直接剪掉。

    前面说过,可以把二维数组处理成为字符串作为主键,也可以把二维数组进行二进制压缩,用int作为主键应该会更快。

    总之,最大的优化就是剪枝,剪枝后时间为0ms。这题用dfs+剪枝也能过,但没能判重,浪费了不少时间。比赛时,写A*代码应该最为简单。

AC代码:

#include<cstdio>#include<cstring>#include<map>#include<queue>#include<algorithm>using namespace std;const int maxn=30;int goal[][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};const int dx[]={1,1,2,2,-1,-1,-2,-2};const int dy[]={2,-2,1,-1,2,-2,1,-1};int v[maxn];struct node{int x,y;node(){}node(int x,int y):x(x),y(y){}};void deal(){v[0]=1;for(int i=1;i<27;++i) v[i]=2*v[i-1];}inline int get1(int (*a)[5]){  //统计位置不对的棋子 int c=0;for(int i=0;i<5;++i)for(int j=0;j<5;++j){if(a[i][j]==2) continue;else {if(a[i][j]!=goal[i][j]) ++c;}}return c; }int get2(int (*a)[5]){  //二进制的值 int c=0;for(int i=0;i<5;++i)for(int j=0;j<5;++j){c+=a[i][j]*v[i*5+j];}return c;} int get3(int (*a)[5]){ //空地的位置 for(int i=0;i<5;++i)for(int j=0;j<5;++j) if(a[i][j]==2) return i*5+j;}void get4(int (*a)[5],int h,int pos){ //解码 h-=2*v[pos];a[pos/5][pos%5]=2;for(int i=0;i<25;++i){if(i==pos) {h=h>>1;continue;}else {a[i/5][i%5]=h&1;h=h>>1;}}}int bfs(int (*a)[5]){if(get1(a)>10) return -1;map<int,node>ha;queue<int>q;int f=get2(a);q.push(f);ha[f]=node(0,get3(a));while(!q.empty()){int h=q.front();q.pop();int b[5][5];node g=ha[h];get4(b,h,g.y);int d1=get1(b);if(d1+g.x>10) continue;if(d1==0) return g.x;int x=g.y/5,y=g.y%5;int old[5][5];for(int i=0;i<8;++i){int nx=x+dx[i],ny=y+dy[i];if(nx<0||ny<0||nx>=5||ny>=5) continue;memcpy(old,b,sizeof(b));swap(old[x][y],old[nx][ny]);int k=get2(old);if(ha.count(k)) continue;q.push(k);ha[k]=node(ha[h].x+1,get3(old));}}return -1;} int main(){deal();int T;scanf("%d",&T);char ch[5][5];int s[5][5];while(T--){for(int i=0;i<5;++i) scanf("%s",ch[i]);for(int i=0;i<5;++i)for(int j=0;j<5;++j){s[i][j]=ch[i][j]-'0';}int ans=bfs(s);if(ans==-1) printf("Unsolvable in less than 11 move(s).\n");else printf("Solvable in %d move(s).\n",ans);}return 0;}

如有不当之处欢迎指出!

0 0