Hdu1401-Solitaire(双向bfs)

Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right respectively.
There are four identical pieces on the board. In one move it is allowed to:
> move a piece to an empty neighboring field (up, down, left or right),
> jump over one neighboring piece to an empty field (up, down, left or right).There are 4 moves allowed for each piece in the configuration shown above. As an example let‘s consider a piece placed in the row 4, column 4. It can be moved one row up, two rows down, one column left or two columns right.
Write a program that:
> reads two chessboard configurations from the standard input,
> verifies whether the second one is reachable from the first one in at most 8 moves,
> writes the result to the standard output.

Input

Each of two input lines contains 8 integers a1, a2, ..., a8 separated by single spaces and describes one configuration of pieces on the chessboard. Integers a2j-1 and a2j (1 <= j <= 4) describe the position of one piece - the row number and the column number respectively. Process to the end of file.

Output

The output should contain one word for each test case - YES if a configuration described in the second input line is reachable from the configuration described in the first input line in at most 8 moves, or one word NO otherwise.

Sample Input

4 4 4 5 5 4 6 5

2 4 3 3 3 6 4 6

Sample Output

YES

题意:棋盘大小为8*8,然后有4个球,给出初始所有球的位置以及目标位置,每次可以移动一个球,要么移动到旁边空的地方,要么跨过一个

球到空的地方(不能跨过多个球),问能否在8步以内到达目标状态。

解析:限定了8步,而且前后搜效果是一样的,所以可以考虑用双向bfs,然后8个数哈希,要注意对4个点要排序。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const
int mod=500007;
const
int maxn=200005;
int
dx[]={-1,0,1,0},dy[]={0,-1,0,1};
bool
in(int x,int y){ return x>=1&&x<=8&&y>=1&&y<=8; }
struct
point   //球的坐标
{

    int
x,y;
    point(int x=0,int y=0):x(x),y(y){}
    bool
operator < (const point& t) const
    {

        if
(x!=t.x) return x<t.x;
        return
y<t.y;
    }
};

struct
node
{

    point p[4];  //每个节点保存4个球的坐标,并且是排好序的
}
nod[2][maxn];   //开二维用于双向搜
struct
Hash
{

    int
v,k,nid,next;   //保存哈希值,0或1,下标,next指针
}
ha[mod+maxn];
int
f[2],r[2],hash_id;  //队首队尾指针
bool
Same(int k1,int a,int k2,int b)  //判断两个结构体是否完全相同
{

    for
(int i=0;i<4;i++)
    {

        if
(nod[k1][a].p[i].x!=nod[k2][b].p[i].x) return false;
        if
(nod[k1][a].p[i].y!=nod[k2][b].p[i].y) return false;
    }

    return
true;
}

int
GetHash(point p[])  //得到哈希值
{

    int
ret=0,k=1;
    for
(int i=0;i<4;i++)
    {

        ret+=p[i].x*k; k*=3;  //k是系数
        ret+=p[i].y*k; k*=3;
    }

    return
ret;
}

int
Insert_Hash(int v,int k,int nid)  //插入
{

    int
a=v%mod;
    int
p=ha[a].next;
    while
(p!=-1)
    {

        if
(ha[p].v==v&&Same(ha[p].k,ha[p].nid,k,nid)) return ha[p].k==k?0:1;  //有相同的状态,k值相同返回0,不同返回1
        p=ha[p].next;
    }

    p=++hash_id;
    ha[p].v=v; ha[p].k=k; ha[p].nid=nid;  //增加新的节点
    ha[p].next=ha[a].next; ha[a].next=p;
    return
-1;  
}

bool
Has(node& t,int x,int y)
{

    for
(int i=0;i<4;i++) if(t.p[i].x==x&&t.p[i].y==y) return true;  //此处是球
    return
false;
}

bool
AddNode(node& t,int i,int j,int k)
{

    int
x=t.p[i].x;
    int
y=t.p[i].y;
    int
nx=x+dx[j];
    int
ny=y+dy[j];
    if
(!in(nx,ny)) return false;   //出界
    if
(Has(t,nx,ny)) nx+=dx[j],ny+=dy[j]; //有球
    if
(!in(nx,ny)) return false;  //出界
    if
(Has(t,nx,ny)) return false; //还有球
    node& tt=nod[k][r[k]];
    tt=t;
    tt.p[i].x=nx; tt.p[i].y=ny;
    sort(tt.p,tt.p+4);   //排序
    int
a=Insert_Hash(GetHash(tt.p),k,r[k]);
    if
(a==1) return true;  //找到解
    else if
(a==-1) r[k]++;  //增加新节点
    return
false
}

bool
bfs(int k)
{

    int
en=r[k];
    while
(f[k]<en)
    {

        node& t=nod[k][f[k]++];
        for
(int i=0;i<4;i++)   //4个球4个方向
            for
(int j=0;j<4;j++) if(AddNode(t,i,j,k)) return true;
    }

    return
false;
}

bool
solve()
{

    if
(Same(0,0,1,0)) return true;  //相同
    int
step=0;
    f[0]=f[1]=0; r[0]=r[1]=1;
    for
(int i=0;i<mod;i++) ha[i].next=-1;
    hash_id=mod-1;
    Insert_Hash(GetHash(nod[0][0].p),0,0);
    Insert_Hash(GetHash(nod[1][0].p),1,0);
    while
(f[0]<r[0]||f[1]<r[1])
    {

        if
(step>=4) return false;
        step++;
        if
(bfs(0)) return true;
        if
(bfs(1)) return true;
    }

    return
false;
}

int
main()
{

    int
x,y;
    while
(scanf("%d%d",&x,&y)!=EOF)
    {

        nod[0][0].p[0]=point(x,y);
        for
(int i=1;i<4;i++)
        {

             scanf("%d%d",&x,&y);
             nod[0][0].p[i]=point(x,y);
        }

        for
(int i=0;i<4;i++)
        {

             scanf("%d%d",&x,&y);
             nod[1][0].p[i]=point(x,y);
        }

        sort(nod[0][0].p,nod[0][0].p+4);
        sort(nod[1][0].p,nod[1][0].p+4);
        if
(solve()) printf("YES\n");
        else
printf("NO\n");
    }

    return
0;
}

时间: 2024-10-07 05:06:51

Hdu1401-Solitaire(双向bfs)的相关文章

HDU1401(双向BFS)

题意:http://acm.hdu.edu.cn/showproblem.php?pid=1401 给你8*8的棋盘和4个棋子初始位置.最终位置,问你能否在8次操作后达到该状态. 思路: 双向BFS,起点开始正搜4步,终点倒搜4步,map标记. 1 #define IOS ios_base::sync_with_stdio(0); cin.tie(0); 2 #include <cstdio>//sprintf islower isupper 3 #include <cstdlib>

UVA-1604 Cubic Eight-Puzzle (双向BFS+状态压缩+限制搜索层数)

题目大意:立体的八数码问题,一次操作是滚动一次方块,问从初始状态到目标状态的最少滚动次数. 题目分析:这道题已知初始状态和目标状态,且又状态数目庞大,适宜用双向BFS.每个小方块有6种状态,整个大方格有9*6^8个状态.每个小方块用一位6进制数表示即可. 注意:状态转移时要谨慎,否则会出现意想不到的错误: 这道题的末状态有256(2^8)个,如果对搜索层数不加限制,即使双向BFS也会TLE的,当限制正向搜索15层逆向搜索15层至正向搜索27层反向搜索3层时都能AC(我下面贴出的程序是这样的),其

HDU1195 双向BFS(或BFS)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1195 , 双向BFS或者直接BFS也可以过. 其实这道题只是单向BFS就可以过的,但是为了练算法,所以还是用了双向BFS来写. 算法: 先预处理一下,从1111到9999的所有点进行构图(由于是1~9的,所以除去含有0元素的数字),能进行一次变换变成的数字则表示两点之间连通.然后从初态与目态两个点进行BFS,如果有轨迹重合的就返回路程和. 这里注意双向BFS要一层一层的进行搜索,不然的话会产生错误,

POJ1915Knight Moves(单向BFS + 双向BFS)

题目链接 单向bfs就是水题 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <queue> 6 using namespace std; 7 const int INF = 0x3f3f3f3f; 8 const int Max = 300 + 5; 9 struct Node 10 { 11 int

BFS、双向BFS和A*

BFS.双向BFS和A* Table of Contents 1. BFS 2. 双向BFS 3. A*算法 光说不练是无用的.我们从广为人知的POJ 2243这道题谈起:题目大意:给定一个起点和一个终点.按骑士的走法(走日字),从起点到终点的最少移动多少次 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2RraXJjaGhvZmY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/g

HDU 1043 Eight(双向BFS+康托展开)

http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用双向BFS来做. ①双向BFS 在单向BFS的基础上,多建一个从终止状态开始搜索的队列,当然这个时候需要两个vis[]辅助数组,分别记录两个队列的访问情况,当两个队列相遇时即可终止循环. ②康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[

POJ 1915-Knight Moves (单向BFS &amp;&amp; 双向BFS 比较)

题目链接:Knight Moves 研究了一下双向BFS,不是很难,和普通的BFS一样,双向BFS不过是从 起点和终点同时开始搜索,可减少搜索时间 当两条搜索路线相遇时,结束. 貌似有一年百度的招聘 笔试,就是双向BFS.... 下面,比较一下BFS 和 双向BFS的用时: BFS STL的queue可能会浪费一点时间 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstrin

第六章部分例题 双向bfs邻接表和邻接矩阵实现

Idealpath 双向bfs输出颜色,邻接矩阵实现 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <map> 6 #include <algorithm> 7 8 using namespace std; 9 10 const int maxn=10000; 11 const int inf=1

[NOIP2002]字串变换 T2 双向BFS

题目描述 已知有两个字串  A,B  及一组字串变换的规则(至多6个规则): A1?>B1 A2?>B2 规则的含义为:在  A$中的子串  A1可以变换为可以变换为B1.A2可以变换为可以变换为B2  -. 例如:A==′abcd′B='xyz' 变换规则为: 'abc'-> 'xu' 'ud'-> 'y' 'y'-> 'yz' 则此时,A可以经过一系列的变换变为可以经过一系列的变换变为B,其变换的过程为: 'abcd'-> 'xud'-> 'xy'->