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

问题 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;
}

目的

一个笔记(写的有点复杂)

猜你喜欢

转载自blog.csdn.net/NP_hard/article/details/110351945