双向广搜 Solitaire——hdu1401

目录

前言

   字符数字的转换

   bfs or double dfs

  棋局的编号

Solitaire

   问题描述

   输入

   输出

问题分析

   判重

   棋子走动逻辑

单向搜索代码

   双向搜索退出条件

   双向广搜代码


前言

        交代一下我写这题的感受,被自己气笑了,本来以为是我字符串没弄好,就总盯着字符串,结果发现连最基本的变量赋值逻辑都弄错了,服了

   字符数字的转换

                                   字符-'0' 字符变数字,数字+'0' 数字变字符

   bfs or double dfs

        这题为什么不能用bfs,其实用bfs还是用double bfs很好判断,就看一下每次入队的状态数量为多少即可

        本题总共有四个棋子,每个棋子都有上下左右四个方向,就意味着单个节点入队最多为4*4=16,种方案,不难发现,这个还是以指数形增长的,如果连续八步那么就会拓展出,16^{8}>1500万种状态,非常恐怖。

        所以要用double bfs减少状态数大约2*16^{4}=131072种棋局,具体关于double bfs分析在这里双向广搜

  棋局的编号

        本题为了方便判重,我对该棋盘的所有格子都进行了编号

12345678
910111213141516
1718192021222324
2526272829303132
3334353637383940
4142434445464748
4950515253545556
5758596061626464

例如四个棋子如果为(1,1),(1,2),(1,3),(1,4)那么我就用string s="1234"代表它

Solitaire

   问题描述

        在一个8*8的棋盘中放入四个棋子,要求八步之内四颗棋子到达指定位置,现给出四颗棋子的初始位置,和最终位置,判断是否可以到达终点

   输入

        总共两行,第一行用八个整数代表四颗棋子的初始位置,第二行用八个整数代表四颗棋子的终止位置

   输出

        如果可以到达终点,那么输出"YES",否则输出"NO"

问题分析

        前面已经说过,本题需要用到双向广搜,我们不妨先实现单向搜索

   判重

        其实这才是我想发这题的题解的原因,本题最重要的就是判重的操作,首先明确一点:绝对不能用int vis[][][][][][][][]判重,这个八位数组占用空间太大,算一下等于32^{8}=104.8576GB,会爆掉

所以次优先的方法就是改用char数组那么就可以减到8^{8}=16MB,勉强可以接受。但如果把这个八位数砍成一个字符串,用map压缩无效空间,那么这个内存占用将会减少到几kb

   棋子走动逻辑

        题目说棋子可以上下左右移动,如果碰到该方向有棋子,那么就往该方向再移动一格(这个操作最多一次),根据代码逻辑就可以编写代码

nx += dir[k][0];
ny += dir[k][1];
if (!check(nx, ny, i,now.s)) {
    nx +=dir[k][0];
    ny +=dir[k][1];
    if (!check(nx, ny, i,now.s)) { //如果还有棋子,continue
        continue;
    }
}

        check函数:

//这个字符串索引逻辑可以参考上图
bool check(int x,int y,int i,string s) {
    for(int k=0;k<4;k++){
        if (k != i) {
            if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
                return false;
            }
        }
    }
    return true;
}

        translate函数

                这个是将棋子位置转化为一个独一无二的字符串(方便判重)的代码

string Translate(string s) {

    vector<int> pos(4);
    for (int i = 0; i < 4; i++) {
        // 将每两位字符转换为一个十进制数
        // 注意:这里假设输入字符串只包含'0'和'1',且长度至少为8
        pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
    }

    sort(pos.begin(), pos.end());

    string a;
    for (int i = 0; i < 4; i++) {
        // 将排序后的十进制数转换回字符串,并拼接到结果字符串中
        a += to_string(pos[i]);
    }

    return a;
}

   单向搜索代码

#include<string>
#include<iostream>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2] = { {0,-1},{0,1},{1,0},{-1,0} };
struct node {
    string s;
    int step;
    node() {}
    node(string ss,int ste):s(ss),step(ste){}
};
map<string, bool>vis;
queue<node>que;
int pos[4];
string m;
string st, ed;
string ed_s;
string st_s;
node now, nxt;
bool flag=false;

string Translate(string s) {

    vector<int> pos(4);
    for (int i = 0; i < 4; i++) {
        // 将每两位字符转换为一个十进制数
        // 注意:这里假设输入字符串只包含'0'和'1',且长度至少为8
        pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
    }

    sort(pos.begin(), pos.end());

    string a;
    for (int i = 0; i < 4; i++) {
        // 将排序后的十进制数转换回字符串,并拼接到结果字符串中
        a += to_string(pos[i]);
    }

    return a;
}

bool check(int x,int y,int i,string s) {
    for(int k=0;k<4;k++){
        if (k != i) {
            if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
                return false;
            }
        }
    }
    return true;
}

void bfs() {
    que.push(node(st, 0));
    m=Translate(st);
    vis[m] = true;
    while (!que.empty()) {
        now = que.front();
        que.pop();
        for(int i=0;i<4;i++){
            //一开始把这行放这了,我是真的服了
            //int nx = int(now.s[2*i] - '0'), ny = int(now.s[2*i + 1] - '0');
            for (int k = 0; k < 4; k++) {
                int nx = int(now.s[2*i] - '0'), ny = int(now.s[2*i + 1] - '0');
                nx += dir[k][0];
                ny += dir[k][1];
                if (!check(nx, ny, i,now.s)) {
                    nx +=dir[k][0];
                    ny +=dir[k][1];
                    if (!check(nx, ny, i,now.s)) {
                        continue;
                    }
                }
                if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
                nxt.s = now.s;
                nxt.s[2*i] = '0'+nx;
                nxt.s[2*i + 1] = '0'+ny;
                nxt.step = now.step + 1;        
                if (nxt.step > 8) continue;
                m = Translate(nxt.s);
                if (nxt.step <= 8 && m == ed_s) {
                    flag = true;
                    return;
                }
                if (!vis[m]) {
                    vis[m] = true;
                    que.push(nxt);
                }
            }
        }
    }
}

//注:字符-'0' 字符变数字,数字+'0' 数字变字符
int main() {
    int i, j;
    for (int k = 1; k <= 8; k++) {
        cin >> i;
        st += '0'+i;
    }
    for (int k = 1; k <= 8; k++) {
        cin >> j;
        ed += '0'+j;
    }
    st_s = Translate(st);
    ed_s = Translate(ed);
    if (st_s == ed_s) {
        cout << "YES" << endl;
        return 0;
    }
    bfs();
    if (flag) {
        cout << "YES" << endl;
    }
    else {
        cout << "NO" << endl;
    }
    return 0;
}

        接下来我们就把它改为双向搜索代码

   双向搜索退出条件

        双向搜索是起点和终点同时同速度出发搜索,那么他们相遇的时候所走的路程一定相同,要求总步数不大于8,那么每个人走的步数也要不大于4,此外我们定义了一个visa判重1代表起点搜过,2代表终点搜过,如果每一个队列搜到了另一个队列的标记,那么也代表他们相遇了

        顺嘴提一句,那个if (nxt.step > 8) continue;的剪枝8也要改成4

   双向广搜代码

#include<string>
#include<iostream>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2] = { {0,-1},{0,1},{1,0},{-1,0} };
struct node {
    string s;
    int step;
    node() {}
    node(string ss,int ste):s(ss),step(ste){}
};
map<string, int>vis;
queue<node>qa;
queue<node>qb;
int pos[4];
string m;
string st, ed;
string ed_s;
string st_s;
node now, nxt;
bool flag=false;

string Translate(string s) {
    vector<int> pos(4);
    for (int i = 0; i < 4; i++) {
        pos[i] = ((s[2 * i] - '0')-1) * 8 + (s[2 * i + 1] - '0');
    }
    sort(pos.begin(), pos.end());
    string a;
    for (int i = 0; i < 4; i++) {
        // 将排序后的十进制数转换回字符串,并拼接到结果字符串中
        a += to_string(pos[i]);
    }
    return a;
}

bool check(int x,int y,int i,string s) {
    for(int k=0;k<4;k++){
        if (k != i) {
            if (x == (s[2*k]-'0') && y == (s[2*k + 1]-'0') ) {
                return false;
            }
        }
    }
    return true;
}

void dul_bfs() {
    qa.push(node(st, 0));
    qb.push(node(ed, 0));
    vis[st_s] = 1;
    vis[ed_s] = 2;
    while (!qa.empty() && !qb.empty()) {
        if (qa.size() < qb.size()) {
            now = qa.front();
            qa.pop();
            for(int i=0;i<4;i++){
                for (int k = 0; k < 4; k++) {
                    int nx = now.s[2*i] - '0', ny = now.s[2*i + 1] - '0';
                    nx += dir[k][0];
                    ny += dir[k][1];
                    if (!check(nx, ny, i,now.s)) {
                        nx +=dir[k][0];
                        ny +=dir[k][1];
                        if (!check(nx, ny, i,now.s)) {
                            continue;
                        }
                    }
                    if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
                    nxt.s = now.s;
                    nxt.s[2*i] = '0'+nx;
                    nxt.s[2*i + 1] = '0'+ny;
                    nxt.step = now.step + 1;        
                    if (nxt.step > 4) continue;
                    m = Translate(nxt.s);
                    if (nxt.step <= 4 && vis[m]==2) {
                        flag = true;
                        return;
                    }
                    if (!vis[m]) {
                        vis[m] = 1;
                        qa.push(nxt);
                    }
                }
            }
        }
        else {
            now = qb.front();
            qb.pop();
            for (int i = 0; i < 4; i++) {
                for (int k = 0; k < 4; k++) {
                    int nx = now.s[2 * i] - '0', ny = now.s[2 * i + 1] - '0';
                    nx += dir[k][0];
                    ny += dir[k][1];
                    if (!check(nx, ny, i, now.s)) {
                        nx += dir[k][0];
                        ny += dir[k][1];
                        if (!check(nx, ny, i, now.s)) {
                            continue;
                        }
                    }
                    if (nx < 1 || nx > 8 || ny < 1 || ny > 8) continue;
                    nxt.s = now.s;
                    nxt.s[2 * i] = '0' + nx;
                    nxt.s[2 * i + 1] = '0' + ny;
                    nxt.step = now.step + 1;
                    if (nxt.step > 4) continue;
                    m = Translate(nxt.s);
                    if (nxt.step <= 4 && vis[m]==1) {
                        flag = true;
                        return;
                    }
                    if (!vis[m]) {
                        vis[m] = 2;
                        qb.push(nxt);
                    }
                }
            }
        }
        
    }
}

//注:字符-'0' 字符变数字,数字+'0' 数字变字符
int main() {
    int i, j;
    for (int k = 1; k <= 8; k++) {
        cin >> i;
        st += '0'+i;
    }
    for (int k = 1; k <= 8; k++) {
        cin >> j;
        ed += '0'+j;
    }
    st_s = Translate(st);
    ed_s = Translate(ed);
    if (st_s == ed_s) {
        cout << "YES" << endl;
        return 0;
    }
    dul_bfs();
    if (flag) {
        cout << "YES" << endl;
    }
    else {
        cout << "NO" << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值