文章目录
问题 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;
}