Hdu1401 Solitaire

题意

有一个8x8的棋盘,上面有4个棋子,棋子可以这样移动:1.移动到相邻空位置 2.跳过一个棋子到其前面,具体看图。然后给你两个棋盘到布局,问布局1是否可以在8步之内移动成布局2的样子?

图片说明

分析

可以想到这是一个最短路问题,每一个棋盘布局就是一种状态,从初始状态移动到目标状态就是一个求最短路的过程。
这题由于给了初始状态和目标状态,所以很容易就想到是双向BFS算法写的话时间复杂度更低,保存的中间状态就更少,所以内存也会更少。

如何存储状态

显然我们可以使用string来存储棋盘,空的位置是0,有棋子的位置是1,然后串起来就可以hash了,但是此处可以看到是8x8=64个位置的棋盘,可以联想到用longlong来存棋盘,每一个位置用一个二进制位来存,这样不同布局,其long long值就不同。为何不选用string是因为,对string哈希需要一定时间,在unorder_map中,而longlong可以直接存取。

AC代码

#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <cstring>
using namespace std;
typedef unsigned long long ull;
int dir[4][2] = {-1,0,1,0,0,1,0,-1};
int dir2[4][2] = {-2,0,2,0,0,2,0,-2};
struct node{
    ull hs;//哈希值
    int st[8][8];//棋盘布局
    int pos[4][2];//棋子位置
}st1,st2;

ull Hash(node &no){ //哈希函数,将棋盘转换成unsigned long long
    ull hs = 0;
    for(int i = 0;i<8;i++){
        for(int j = 0;j<8;j++){
            hs = hs<<1| no.st[i][j];
        }
    }
    return hs;
}

int ex(queue<node> &q,unordered_map<ull,int> &mp1,unordered_map<ull,int> &mp2){
    node cur = q.front();q.pop();
    for(int i = 0;i<4;i++){
        int x =  cur.pos[i][0],y = cur.pos[i][1];
        for(int j = 0;j<4;j++){
            int dx = x+dir[j][0];
            int dy = y+dir[j][1];
            if(dx>=0 && dx<8 && dy>=0 && dy<8 && cur.st[dx][dy] == 0){ //尝试距离为1的位置
                node ne = cur;
                ne.st[x][y] = 0;
                ne.st[dx][dy] = 1;
                ne.pos[i][0] = dx,ne.pos[i][1] = dy;
                ne.hs = Hash(ne);
                if(!mp1.count(ne.hs)){
                    mp1[ne.hs] = mp1[cur.hs]+1;
                    q.push(ne);
                    if(mp2.count(ne.hs))return mp1[ne.hs]+mp2[ne.hs];
                }
            }else if(dx>=0 && dx<8 && dy>=0 && dy<8 && cur.st[dx][dy] == 1){
                dx = x+dir2[j][0];
                dy = y+dir2[j][1];
                if(dx>=0 && dx<8 && dy>=0 && dy<8 && cur.st[dx][dy] == 0){//尝试距离为2的位置
                    node ne = cur;
                    ne.st[x][y] = 0;
                    ne.st[dx][dy] = 1;
                    ne.pos[i][0] = dx,ne.pos[i][1] = dy;
                    ne.hs = Hash(ne);
                    if(!mp1.count(ne.hs)){
                        mp1[ne.hs] = mp1[cur.hs]+1;
                        q.push(ne);
                        if(mp2.count(ne.hs)) return mp1[ne.hs]+mp2[ne.hs];
                    }
                }
            }
        }
    }
    return -1;
}
int bfs(){
    unordered_map<ull,int> dis1,dis2;
    queue<node> q1,q2;
    q1.push(st1);q2.push(st2);
    dis1[st1.hs] = 0;dis2[st2.hs] = 0;

    while(q1.size() && q2.size()){
        if(dis1[q1.front().hs] + dis2[q2.front().hs]>8) return -1;
        int res;
        if(q1.size() <= q2.size()) res = ex(q1,dis1,dis2);
        else res = ex(q2,dis2,dis1);
        if(res!= -1 && res<=8) return res;
        if(res>8) return -1;
    }
    return -1;
}

int main(){
    int x,y;
    while(~scanf("%d %d",&x,&y)){
        x--;y--; //减一,是因为我想从下标0开始
        memset(st1.st,0,sizeof(st1.st));
        memset(st2.st,0,sizeof(st2.st));
        st1.st[x][y] = 1;
        st1.pos[0][0] = x,st1.pos[0][1] = y; //存储棋子的位置
        for(int i = 1;i<=3;i++){
            scanf("%d %d",&x,&y);x--;y--;
            st1.st[x][y] = 1;
            st1.pos[i][0] = x,st1.pos[i][1] = y;
        }
        for(int i = 0;i<4;i++){
            scanf("%d %d",&x,&y);x--;y--;
            st2.st[x][y] = 1;
            st2.pos[i][0] = x,st2.pos[i][1] = y;
        }
        st1.hs = Hash(st1);
        st2.hs = Hash(st2);
        if(st1.hs == st2.hs)
            puts("YES");
        else{
            int res = bfs();
            if(res == -1) puts("NO");
            else {
                puts("YES");
            }
        }
    }
    return 0;
}