hdu1401“Solitaire“

hdu1401"Solitaire"(双向广搜)

题意

有一个8X8的棋盘,上面有4个棋子,棋子可以上下左右移动。给定一个初始状态和一个目标状态,问能否在8步之内到达。

注意点(我的错误)
  • 移动一步,棋子落到另一个棋子上是,才可以移动两步。(容易理解为即可以移动一步,也可以移动两步。)
  • 棋子不区分顺序,所以需要按一定规则排序后才可以确定一个状态。
思路
  • 使用8维数组vis*标记,进行查重。
  • 有八个方向向量vex*。
  • 伪双向广搜:并不是真的从两个初始一起搜索,分别从目标状态、初始状态使用bfs搜索4步,它们的标记有交集即返回true。
  • bfs(出发状态start,搜索时标记sign,目标标记endsign):两次广搜,其实就是出发点不同,搜索时进行所作的标记不同,停止标记不同而已,可以归纳成一个bfs()使用两次。
代码:
#include<iostream>
using namespace std;
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
const int vex[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};//方向向量
char vis[8][8][8][8][8][8][8][8];
struct Chess//棋子
{
    int x, y;
};
struct Node//状态
{
    Chess ches[4];
    int step;
};
class cmp//谓词类:小于//装了个X,可以直接写cmp函数的
{
public:
    bool operator()(Chess c1, Chess c2){
        if(c1.x == c2.x)
            return c1.y < c2.y;
        else
            return c1.x < c2.x;
    }
};
void sortNode(Node& node)//排序数据之后才是状态
{
    sort(node.ches, node.ches+4, cmp());//cmp()是一个匿名类!
}
bool movee(Node& next, int c, int v)//chess[c]向vex[v]移动一步
{
    next.ches[c].x += vex[v][0];
    next.ches[c].y += vex[v][1];
    if(next.ches[c].x < 1 || next.ches[c].x > 8 || next.ches[c].y < 1 || next.ches[c].y > 8){
        return false;//棋盘越界
    }
    for(int i = 0; i < 4; i++){
        if(i != c && next.ches[c].x == next.ches[i].x && next.ches[c].y == next.ches[i].y){
            return false;//重叠
        }
    }
    return true;
}
bool tGetNode(Node& next, Node& src, int c, int v)//根据参数尝试获取一个状态,成功返回true
{
    for(int i = 0; i < 4; i++){
        next.ches[i] = src.ches[i];
    }
    next.step = src.step + 1;
    //棋子移动,当不成功是可以在移动一步
    for(int i = 0; i < 2; i++){
        if(movee(next, c, v)){//成功
            sortNode(next);
            return true;
        }
    }

    return false;//移动失败
}
char& atVis(Chess ches[])//根据状态返回vis相应一个位置引用:不用每次都写8维数组。注意减去1
{
    return vis[ches[0].x-1][ches[0].y-1][ches[1].x-1][ches[1].y-1][ches[2].x-1][ches[2].y-1][ches[3].x-1][ches[3].y-1];
}
//保存开始状态,目标状态
Node beg, tar;

//从start状态进行广搜,已经搜索的标记sign,遇到endsign就成功,返回true
bool bfs(Node& start, char sign, char endsign)
{
    Node node, next;
    queue<Node> q;
    q.push(start);
    atVis(start.ches) = sign;//标记

    while(!q.empty()){
        node = q.front();
        q.pop();

        if(node.step < 4){//还没到4步
            for(int c = 0; c < 4; c++)//棋子维度
            for(int v = 0; v < 4; v++){//方向维度
                if(tGetNode(next, node, c, v)){//存在该转移方法的一个状态
                    if(atVis(next.ches) == 0){//未被搜索过
                        atVis(next.ches) = sign;//标记
                        q.push(next);
                    }
                    else if(atVis(next.ches) == endsign){//找到结束标志
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    while(~scanf("%d%d", &beg.ches[0].x, &beg.ches[0].y)){//每一个案例
        for(int i = 1; i < 4; i++)
            scanf("%d%d", &beg.ches[i].x, &beg.ches[i].y);
        sortNode(beg);
        beg.step = 0;

        for(int i = 0; i < 4; i++)
            scanf("%d%d", &tar.ches[i].x, &tar.ches[i].y);
        sortNode(tar);
        tar.step = 0;

        memset(vis, 0, sizeof(vis));
	//第一个bfs搜索后标记‘2’,作为第二个bfs的搜索目标(本质)
	//第一个bfs返回false(因为没有‘1’)
        if(bfs(tar,'2', '1') || bfs(beg, '1', '2'))
            printf("YES\n");//第二个bfs返回了true
        else
            printf("NO\n");//第二个bfs返回了false
    }
    return 0;
}

说一个令人兴奋的错误思路

在一个二维空间上,有一个点,可以上下左右四个方向可以移动一步。给出一个初始点和一个终点,问在在n步之内能否到达。
对于这题,非常简单,一个状态只由一个 点决定(hdu1401是4个点)。vis*是一个2维数组。
注意到,一个点1x2维,四个点4x2=8维,所以!!!hdu1401在某种意义下可以看作给定两个点(两种状态),问在8维空间中 8步之内初始状态(初始点)能否到达给定终点状态?
而且,因为空间中没有障碍,可以根据曼哈顿距离直接使用贪心法!!不用暴力的bfs,岂不美滋滋??!
事实上这是错的:

  1. 四个棋子是无顺序的,需要排序。在8维空间图上看,相邻的两个结点(状态)它们之间的花费不一定比不相邻的小!即状态的扩散不是连续的。而曼哈顿距离只是空间距离的一种评估,所以不能作为hdu1401的贪心评估函数!也就不能根据这个写贪心法。不过,一个多点状态转化为更高维的单点状态这个思想还是挺有意思的。
突然想到的另一个点:

关于A*算法(bfs+贪心),应该使用的是优先队列priority_queue.

//伪代码
bfsA(状态 star, 状态 tar, 标记 sign, 搜索目标标记 tarsign){
	q.push(star);
	atVis(star) = sign;
	
	while(!q.empty()){
		node = q.top();
		q.pop();
		
		for(遍历步长临近的点){
			if(tryGetNext(next, node)){
				//tryGetNext:尝试获取下一个状态,评估函数最优是优先级最高
				if 搜索成功 atVis(next) == tarsign
					退出,返回结果
				else
					q.push(next); 
					atVis(next) = sign;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值