双广搜

题目链接
题目大意:给你4个棋子的坐标,给你最终状态的4个坐标,每个棋子可以走4个方向,入过下一步是空格则可以走,想一步是棋子则走到对应的下一个位置(要是空格才可以,是棋子就不可以),问在8步之内能否走到;
思路:开8维char数组记录4个棋子的坐标,int会MLE;同时从两个方向搜索,如果在8步内某个状态搜到了相反状态则找到答案,否则没有答案;sort是用来简化状态的比如1 1 2 2 3 3 4 4和3 3 4 4 2 2 1 1是一样的,不sort状态会很多;我定义的初始状态向末态的状态是1相反为2;
我看有的人单想广搜也能过,就是要加上剪枝,当前状态到最终状态的最少步数,比剩余步数多则减掉;
可以将两个点放在同一个队列里,我是用了两个队列,先将初始状态走4步,得到标记后,用第二个搜第一个状态;一个队列应该不会炸;

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char flag[8][8][8][8][8][8][8][8];
int z[4][2]= {-1,0,1,0,0,-1,0,1};
struct zp
{
    int x,y;//坐标
};
struct zp1
{
    zp cp[4];//4个棋子的坐标
    int step;//步数
    char num;//哪种状态
} s,e;
int cmp(zp a,zp b)//用来给4个棋子排序减少状态
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}
int check(zp1 a,int i,int j)//判断下一步是什么
{
    int x=a.cp[i].x+z[j][0],y=a.cp[i].y+z[j][1];
    if(x>=0&&x<8&&y>=0&&y<8)
    {
        for(int k=0; k<4; k++)
        {
            if(k!=i&&a.cp[k].x==x&&a.cp[k].y==y)
                return 1;//是棋子
        }
        return 2;//是空格
    }
    return 0;//越界
}
void make_set(zp1 a)//标记
{
    flag[a.cp[0].x][a.cp[0].y][a.cp[1].x][a.cp[1].y][a.cp[2].x][a.cp[2].y][a.cp[3].x][a.cp[3].y]=a.num;
}
int get_ans(zp1 a)//是否得到答案
{
    char ss=flag[a.cp[0].x][a.cp[0].y][a.cp[1].x][a.cp[1].y][a.cp[2].x][a.cp[2].y][a.cp[3].x][a.cp[3].y];
    if(ss==a.num)//相同状态的访问
        return -1;
    else if(ss=='0')//没有访问过
        return -2;
    else return 1;//不同状态的访问
}
int dbfs()
{
    memset(flag,'0',sizeof(flag));
    queue<zp1> q[2];
    s.step=0,s.num='1';
    e.step=0,s.num='2';
    make_set(s);
    if(get_ans(e)==1)//起始状态与终止状态相同
        return 1;
    make_set(e);
    q[0].push(s);
    q[1].push(e);
    int in=0;
    while((!q[0].empty())||(!q[1].empty()))//伪双广搜
    {
        if(q[0].empty()) in=1;
        zp1 u=q[in].front();
        q[in].pop();
        if(u.step>=4)
            continue;
        for(int i=0; i<4; i++)//4个棋子
        {
            for(int j=0; j<4; j++)//4个方向
            {
                zp1 v=u;
                int ss=check(v,i,j);//判断v状态第i个棋子走j方向的状态
                if(ss==1)//棋
                {
                    v.cp[i].x+=z[j][0];
                    v.cp[i].y+=z[j][1];
                    ss=check(v,i,j);
                    if(ss==2)//空
                    {
                        v.cp[i].x+=z[j][0];
                        v.cp[i].y+=z[j][1];
                        sort(v.cp,v.cp+4,cmp);
                        v.step++;
                        ss=get_ans(v);//判断v状态是否出现过
                        if(ss==-1)//相同状态出现
                            continue;
                        else if(ss==1)//不同状态出现
                            return 1;
                        make_set(v);//标记
                        q[in].push(v);
                    }
                }
                else if(ss==2)//空
                {
                    v.cp[i].x+=z[j][0];
                    v.cp[i].y+=z[j][1];
                    sort(v.cp,v.cp+4,cmp);
                    v.step++;
                    ss=get_ans(v);
                    if(ss==-1)
                        continue;
                    else if(ss==1)
                        return 1;
                    make_set(v);
                    q[in].push(v);
                }
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d",&s.cp[0].x,&s.cp[0].y))//注意下标要减11-8变成0-7
    {
        s.cp[0].x--;
        s.cp[0].y--;
        for(int i=1; i<4; i++)
        {
            scanf("%d%d",&s.cp[i].x,&s.cp[i].y);
            s.cp[i].x--;
            s.cp[i].y--;
        }
        for(int i=0; i<4; i++)
        {
            scanf("%d%d",&e.cp[i].x,&e.cp[i].y);
            e.cp[i].x--;
            e.cp[i].y--;
        }
        sort(s.cp,s.cp+4,cmp);
        sort(e.cp,e.cp+4,cmp);
        if(dbfs())
            printf("YES\n");
        else
            printf("NO\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值