搜索树--板子题

这篇博客通过搜索树的方法详细介绍了如何解决三个经典问题:过河问题使用BFS求解,八数码问题寻找最少移动次数,以及计算国际象棋骑士的最短路径。每个问题都提供了题目描述、输入输出样例和解题代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题 A: 过河问题–搜索树

题目描述

多个囚犯参与者要过河,其中只有监管者一人可以划船。小船每次最多载两人过河。监管者不在时,已有积怨的囚犯可能会斗殴。请问他们该如何安全过河?

假设一开始所有人都在河的左岸,用0表示,如果成功过河,则到达河的右岸,用1表示。

请采用BFS求解,并输出过河过程。

输入

首先输入要过河的人数n(包括监管者和囚犯)

接着输入监管者的编号s(假设每个人的编号从0开始,编号最小的在最右边)

然后输入有积怨的囚犯的对数m

接下来m行,两两输入有积怨的囚犯编号

输出

如有解,输出划船过河方案,即每一步的状态,也就是每个人此时在河的左岸还是右岸。初始状态全部为0。

否则,输出No solution

样例输入1

4
3
2
0 1
1 2

样例输入2

5
4
2
1 3
0 1

样例输出1

0000
1010
0010
1011
0001
1101
0101
1111

样例输出2

00000
10010
00010
10011
00001
11001
01001
11101
01101
11111

代码

#include <iostream>
#include <queue>
#include <vector>
#include <stack>
using namespace std;

class node
{
public:
    int* boat;
    node* father;
    node(int n)
    {
        boat=new int[n];
        for(int i=0;i<n;i++)boat[i]=0;//可优化
    }
};

class solution
{
public:
    int num,boss,pairs;//过河的人数,监管者编号,有仇者对数
    int s1[10],s2[10];//有仇者关系数组
    vector<node*> visit;//已访问结点
    queue<node*> list;//结点队列,bfs
    bool is_same(node* temp1)//判断是否到达目标状态
    {
        for(int i=0;i<num;i++)
            if(temp1->boat[i]!=1)return false;
        return true;
    }
    bool is_same(node* temp1,node* temp2)//重载,判断两个状态是否相等
    {
        for(int i=0;i<num;i++)
            if(temp1->boat[i]!=temp2->boat[i])return false;
        return true;
    }
    void put_next(node* temp1,int i)//将子状态压入队列
    {
        /*初始化子结点*/
        node* subnode=new node(num);//应该在堆中开辟
        for(int j=0;j<num;j++)
            subnode->boat[j]=temp1->boat[j];
        subnode->father=temp1;
        /*确定子结点的情况*/
        int index1=num-boss-1;//boss物理坐标
        int index2=i;//数组第i位
        if(index2==index1){//boss自己过河的情况
            subnode->boat[index1]=subnode->boat[index1]==1?0:1;
        }else if(temp1->boat[index1]==temp1->boat[index2]){//boss和囚犯在河的同一边的情况
            subnode->boat[index1]=subnode->boat[index1]==1?0:1;
            subnode->boat[index2]=subnode->boat[index2]==1?0:1;
        }else{//遍历到的情况是boss和囚犯不在河的同一边,这种情况直接跳过
            return;
        }
        /*判定有没有出现过*/
        bool flag=false;
        for(int j=0;j<visit.size();j++){//判断子结点有没有出现过
            if(is_same(subnode,visit[j])){flag=true;break;}
        }
        /*判定会不会打架*/
        for(int j=0;j<pairs;j++){
            int indexA=num-s1[j]-1;//囚犯1物理地址
            int indexB=num-s2[j]-1;//囚犯2物理地址
            if((subnode->boat[index1]!=subnode->boat[indexA])&&(subnode->boat[indexA]==subnode->boat[indexB])){
                flag=true;
                visit.push_back(subnode);//不合理的以后也别加入队列了
                break;
            }
        }
        if(!flag)list.push(subnode);//如果没有出现过且囚犯不会打架
    }
    void print_path(node* temp)
    {
        stack<node*> bucket;
        node *p=temp;
        while(p){
            bucket.push(p);
            p=p->father;
        }
        while(!bucket.empty()){
            for(int i=0;i<num;i++)
                cout << bucket.top()->boat[i];
            bucket.pop();
            cout << endl;
        }
    }
    void serach()
    {
        node* start=new node(num);
        start->father=NULL;//根节点
        list.push(start);
        while(!list.empty()){
            if(is_same(list.front())){print_path(list.front());return;};//找到路径
            visit.push_back(list.front());//标记已经访问
            for(int i=num-1;i>=0;i--){//遍历数组下标
                put_next(list.front(),i);//将子状态压入队列
            }
            list.pop();//弹出队列
        }
        cout << "No solution";
    }
};

int main()
{
    solution task;
    cin >> task.num;
    cin >> task.boss;
    cin >> task.pairs;
    for(int i=0;i<task.pairs;i++)
        cin >> task.s1[i] >> task.s2[i];
    task.serach();
    return 0;
}

问题 B: 八数码问题–搜索树

题目描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。
给出一种初始状态S0和目标状态Sg,请找到一种最少步骤的移动方法,实现从初始状态S0到目标状态Sg的转变。
在这里插入图片描述

输入

输入测试次数t

对于每次测试,首先输入一个初始状态S0,一行九个数字,空格用0表示。然后输入一个目标状态Sg,一行九个数字,空格用0表示。

输出

只有一行,该行只有一个数字,表示从初始状态S0到目标状态Sg需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

样例输入

2
283104765
123804765
283104765
283164705

样例输出

4
1

代码

#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <cstring>
using namespace std;

struct node
{
    string chess;
    int move;
};

int s1[4]={-1,1,0,0};
int s2[4]={0,0,-1,1};

class Solution
{
public:
    node start,end;
    int num;//移动次数
    vector<node> visit;//装载已经访问过的结点
    queue<node> list;//装状态结点的队列
    void get_next(int x,int y,node temp1,int index)//将自己的子状态图塞入队列
    {
        if(x>=0&&x<=2&&y>=0&&y<=2){
            int a=x*3+y;//得到交换点的一维坐标
            char tempchar[10]={0};
            strcpy(tempchar,temp1.chess.c_str());
            char c=tempchar[index];
            tempchar[index]=tempchar[a];
            tempchar[a]=c;
            string upstring(tempchar);
            node t1;
            t1.chess=upstring;
            t1.move=temp1.move+1;
            if(!isexist(t1))//如果这个棋盘没出现过
                list.push(t1);
        }
    }
    bool isexist(node t)
    {
        for(int i=0;i<visit.size();i++)
            if(t.chess.compare(visit[i].chess)==0)return true;
        return false;
    }
    int find(string temp)
    {
        for(int i=0;i<temp.length();i++)
            if(temp[i]=='0')return i;
    }
    int Serach()
    {
        list.push(start);
        while(!list.empty()){
            if(list.front().chess.compare(end.chess)==0)//达到目标状态
                return list.front().move;
            visit.push_back(list.front());//标记已经访问过
            int index=find(list.front().chess);//一维的0的坐标
            int x=index/3,y=index%3;
            for(int i=0;i<4;i++)
                get_next(x+s1[i],y+s2[i],list.front(),index);//将自己的子状态图塞入队列
            list.pop();//移出队列
        }
    }
};

int main()
{
    int t;
    cin >> t;
    while(t--){
        Solution problem;
        cin >> problem.start.chess;
        cin >> problem.end.chess;
        problem.start.move=0;
        cout << problem.Serach() << endl;
    }
    return 0;
}

问题 C: 骑士

题目描述

国际象棋中骑士的走法如图所示。
请计算给定骑士在棋盘上的起点,走到终点所需最少步数。
在这里插入图片描述

输入

每个测试包括一行,为用空格隔开的起点和终点。每个点由字母表示的列+数字表示的行组成。

输出

最少步数

样例输入

e2 e4
a1 b2
b2 c3
a1 h8

样例输出

2
4
2
6

代码

#include <iostream>
#include <string>
#include <sstream>
#include <queue>
#include <vector>
using namespace std;

int sx[8]={1,2,2,1,-1,-2,-2,-1};
int sy[8]={2,1,-1,-2,-2,-1,1,2};//用来遍历子结点

struct node
{
    int x,y;
    int step;
};

class solution
{
public:
    queue<node> list;//队列
    vector<node> visit;//已经访问过的结点
    void put_next(int x,int y,int step)
    {
        if((x>=1&&x<=8&&y>=1&&y<=8)&&(!isexist(x,y))){//该节点在棋盘内且没有走过
            node temp2;
            temp2.x=x,temp2.y=y;
            temp2.step=step+1;//步数加一
            list.push(temp2);
        }
    }
    bool isexist(int x,int y)
    {
        for(int i=0;i<visit.size();i++)
            if(visit[i].x==x&&visit[i].y==y)return true;
        return false;
    }
    int search(node a,node b)//返回a到b的最少步数
    {
        a.step=0;
        list.push(a);
        while(!list.empty()){
            node temp1=list.front();
            visit.push_back(temp1);//标记已经访问过了
            if(temp1.x==b.x&&temp1.y==b.y)return temp1.step;//到达指定位置
            for(int i=0;i<8;i++)
                put_next(temp1.x+sx[i],temp1.y+sy[i],temp1.step);//将子结点压入队列
            list.pop();//弹出队列
        }
    }
};

int main()
{
    for(int i=0;i<4;i++){
        string a,b;
        solution task;
        cin >> a >> b;
        node a1,b1;
        a1.x=a[0]-'a'+1;
        a1.y=a[1]-'1'+1;
        b1.x=b[0]-'a'+1;
        b1.y=b[1]-'1'+1;
        cout << task.search(a1,b1) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值