hdu 1401 (双广)

      题意: 在一个8*8的棋盘里有4个棋子,给你这4个棋子的初状态和末状态,问你能否在8步内由初状态转换到末状态,每一步可移动一个棋子到(上下左右)相邻的空格子里,若相邻格子已有棋子,可跳过该棋子,但只可跳过一个棋子,若跳过后仍有棋子,则该方向不可走。

     分析:红果果的搜索题,但别看只走8步感觉状态挺少的,细算一下有(4*4)^8=2^32个状态!!再加上初,末状态已知,因此用双向广搜效率要高些,可把状态减少到2*(4*4)^4=2^17,减少的可不是一点半点。

      双向广搜是指从头,尾开始搜索,若搜索的过程中两者出现了交叉,则说明能实现状态转换。这里为了方便,我先搜索从起点出发的4步,并将搜索结果标记起来,再从终点开始搜索,若发现前面标记过了的状态,则说明状态可达。

      对于状态的储存用了二进制压缩,由于坐标都是从0到7,用三位二进制位就可以表示,8个坐标则总共是24个二进制位,一个int型就可以储存一个状态了。

    最后还有注意的一点是,在储存状态时一定要先对坐标排序,以免出现重复,比如 (2,3),(2,4),(5,6),(5,7)与(2,4),(5,6),(5,7),(2,3)虽然顺序不同,但都是同一个状态。

    代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
const int N=10;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
map<int,int> m;
struct node
{
    int x,y;
    bool check()
    {
        if (x>=0 && x<8 && y>=0 && y<8) return true;
        else return false;
    }
};
typedef struct State
{
    node pos[4];
    int step;
}state;
bool cmp(node a,node b)
{
    return a.x!=b.x?a.x<b.x:a.y<b.y;
}
int set_hash(node *a)
{
    int t=0;
    sort(a,a+4,cmp);
    for (int i=0;i<4;i++) {
        t|=(a[i].x<<(6*i));
        t|=(a[i].y<<(6*i+3));
    }
    return t; //printf("%o\n",t);
}
bool judge(int x,int y,node *a,int v)
{
    for (int k=0;k<4;k++) if (k!=v) {
        if (a[k].x==x && a[k].y==y) return false;
    }
    return true;
}
bool bfs(int kind,state a)
{
    int i,j;
    int t=set_hash(a.pos);
    if (kind==2){
        if (m[t]==1) return true;
    }
    m[t]=kind;
    queue<state> q;
    q.push(a);
    while(!q.empty())
    {
        state u=q.front(); q.pop();
        if (u.step>=4) continue;
        for (i=0;i<4;i++)
            for (j=0;j<4;j++)
            {
                state v=u; v.step++;
                v.pos[i].x+=dx[j];
                v.pos[i].y+=dy[j];
                if (!v.pos[i].check()) continue;
                if (judge(v.pos[i].x, v.pos[i].y, u.pos, i)){
                    t=set_hash(v.pos);
                    if (kind==1) {
                        if (m[t]!=1) {  m[t]=1; q.push(v); }
                    } else {
                        if (m[t]==1) return true;
                        else if (m[t]!=2) { m[t]=2; q.push(v); }
                    }
                } else {
                    v.pos[i].x+=dx[j];
                    v.pos[i].y+=dy[j];
                    if (!v.pos[i].check() || !judge(v.pos[i].x, v.pos[i].y, u.pos, i)) continue;
                    t=set_hash(v.pos);
                    if (kind==1) {
                        if (m[t]!=1) {  m[t]=1; q.push(v); }
                    } else {
                        if (m[t]==1) return true;
                        else if (m[t]!=2) { m[t]=2; q.push(v); }
                    }
                }
            }
    }
    return false;
}
int main()
{
    int i,j,k;
    state s,e;
    while(cin>>s.pos[0].x>>s.pos[0].y)
    {
        s.pos[0].x--; s.pos[0].y--;
        for (i=1;i<4;i++){ cin>>s.pos[i].x>>s.pos[i].y; s.pos[i].x--; s.pos[i].y--; }
        for (i=0;i<4;i++){ cin>>e.pos[i].x>>e.pos[i].y; e.pos[i].x--; e.pos[i].y--; }
        s.step=0; e.step=0;
        bfs(1,s);
        printf("%s\n",bfs(2,e)?"YES":"NO");
        m.clear();
    }
}



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值