《算法笔记》8.2小节——搜索专题->广度优先搜索(BFS)

模板


问题 : 走迷宫(BFS)

时间限制: 1 Sec  内存限制: 128 MB
提交: 639  解决: 207
[提交][状态][讨论版][命题人:外部导入]

题目描述
  有一个n*m格的迷宫(表示有n行、m列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这n*m个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-l表示无路)。
  请统一用 左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0)

输入

第一行是两个数n,m( 1 < n , m < 15 ),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。 

输出

  所有可行的路径,描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示方向。 
  如果没有一条可行的路则输出-1。

样例输入

5 6
1 0 0 1 0 1
1 1 1 1 1 1
0 0 1 1 1 0
1 1 1 1 1 0
1 1 1 0 1 1
1 1
5 6

样例输出

(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)

AC代码及思路

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node{
    int x, y;
    int step;
};
node s, e;
const int maxn=20;
int maze[maxn][maxn];
int inq[maxn][maxn];
int dx[]={0,-1,0,1};
int dy[]={-1,0,1,0};
int BFS(){
    queue<node> q;
    q.push(s);
    inq[1][1]=1;
    while(!q.empty()){
        node now=q.front();
        q.pop();
        if(now.x==e.x && now.y==e.y){
            return now.step;
        }
        for(int i=0; i<4; i++){
            node next={now.x+dx[i], now.y+dy[i], now.step+1};
            if(maze[next.x][next.y] && !inq[next.x][next.y]) {
                q.push(next);
                inq[next.x][next.y]=1;
            }
        }
    }
    return -1;
}
int main(){
    int m, n;
    while(~scanf("%d%d", &m, &n)){
        memset(maze, 0, sizeof(maze));
        memset(inq, 0, sizeof(inq));
        for(int i=1; i<=m; i++){
            for(int j=1; j<=n; j++){
                scanf("%d", &maze[i][j]);
            }
        }
        scanf("%d%d%d%d", &s.x, &s.y, &e.x, &e.y);
        s.step=0;
        printf("%d\n", BFS());
    }
    return 0;
}

问题 A: Jugs 倒水问题

时间限制: 1 Sec  内存限制: 32 MB
提交: 222  解决: 0
[提交][状态][讨论版][命题人:外部导入]

题目描述

In the movie "Die Hard 3", Bruce Willis and Samuel L. Jackson were confronted with the following puzzle. They were given a 3-gallon jug and a 5-gallon jug and were asked to fill the 5-gallon jug with exactly 4 gallons. This problem generalizes that puzzle.

    You have two jugs, A and B, and an infinite supply of water. There are three types of actions that you can use: (1) you can fill a jug, (2) you can empty a jug, and (3) you can pour from one jug to the other. Pouring from one jug to the other stops when the first jug is empty or the second jug is full, whichever comes first. For example, if A has 5 gallons and B has 6 gallons and a capacity of 8, then pouring from A to B leaves B full and 3 gallons in A.

    A problem is given by a triple (Ca,Cb,N), where Ca and Cb are the capacities of the jugs A and B, respectively, and N is the goal. A solution is a sequence of steps that leaves exactly N gallons in jug B. The possible steps are

    fill A 
    fill B 
    empty A 
    empty B 
    pour A B 
    pour B A 
    success

    where "pour A B" means "pour the contents of jug A into jug B", and "success" means that the goal has been accomplished.

    You may assume that the input you are given does have a solution.

 

输入

Input to your program consists of a series of input lines each defining one puzzle. Input for each puzzle is a single line of three positive integers: Ca, Cb, and N. Ca and Cb are the capacities of jugs A and B, and N is the goal. You can assume 0 < Ca <= Cb and N <= Cb <=1000 and that A and B are relatively prime to one another. 

输出

Output from your program will consist of a series of instructions from the list of the potential output lines which will result in either of the jugs containing exactly N gallons of water. The last line of output for each puzzle should be the line "success". Output lines start in column 1 and there should be no empty lines nor any trailing spaces.

样例输入

3 7 1
9 32 6

样例输出

fill B
pour B A
empty A
pour B A
success
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
success

AC代码及思路

  1. BFS适用于求解本题。因为BFS求解出的第一个解必是最短路径(最优解),DFS则不然。
  2. 本题具有马尔科夫性质,当前状态仅与上一状态和倒水动作有关。利用此特性,当碰到重复状态,不必入队列(相当于搜索中进行剪枝),大大降低计算时空复杂度。原本的计算复杂度时随着路径的长度指数增长的。
  3. BFS中,队列的使用是关键,保证搜索的有序性。但在此题中,我们还需输出最优路径,需保存路径信息,而STL队列没有此功能,一旦出列,路径结点就消失了。故笔者采用数组和队首队尾指针来构造队列(模拟pop(), front(), end()),这般的话,入队过的结点通通存储在数组里了,方便我们还原链表存储形式的路径。

这两行代码 要保持严格的顺序,调换顺序后的计算结果不同。

a=a+b-cb; 
b=cb;

AC代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1e5+10;
const int smaxn = 1010;
int table[smaxn][smaxn];
int front=0, last=0;
struct node{
    int a, b;
    int op;
    int pre;	//上一个状态
}tree[maxn];	//用数组的,线性存储结构来构造队列。
node manipulate(node now, int way, int ca, int cb){
    int a = now.a;
    int b = now.b;
    node next;
    switch(way){
        case 1: a=ca; break;	//fill A 
        case 2: b=cb; break;	//fill B
        case 3: a=0; break;	//empty A 
        case 4: b=0; break;	//empty B
        case 5:if(a+b-cb>=0) {a=a+b-cb; b=cb;} //pour A B 判断溢出
            else {b=a+b; a=0;} break;
        case 6: if(a+b-ca>=0) {b=a+b-ca; a=ca;} //pour B A 判断溢出
            else {a=a+b; b=0;} break;
    }
    next.a = a;
    next.b = b;
	next.op = way;
    return next;
}
int BFS(int ca, int cb, int n){
	//初始化
	node ans[100];
	memset(table, 0, sizeof(table));
	front=last=0;
    node S={0};
	//在tree的基础上,添加两个指针,实现队列的操作。采用线性存储结构来构造队列。
	//last指向队尾,front指向队首;
	//S入队列
	tree[last++] = S;
	//当队列为空时中断循环,即队首指针与队尾指针 重合。
	while(front != last){
		node now = tree[front];
		if(now.b == n) {
			return front;
		}
		table[now.a][now.b] =1; //标记当前状态 为 访问过,入队过。
		//对当前状态 执行6种操作,得到6个下一状态。
		for(int i=1; i<=6; i++){
			node next = manipulate(now, i, ca, cb);
			next.pre = front;
			if(table[next.a][next.b] == 0){
				tree[last++] = next;
				table[next.a][next.b] = 1; //标记当前状态 为 访问过。
			}//当前状态,如果不曾访问,曾入队列 待阅兵。反之跳过。
		} 
		//队首出列类比于pop(),以便得到队列的下一队首。
		front++;
	}
    return -1;
}
char strs[7][20]={"", "fill A\n", "fill B\n", "empty A\n", "empty B\n", "pour A B\n", "pour B A\n"};
void print(int index){
	if(index == -1) {
		//printf("FAIL\n"); 
		return;
	}
	stack<int> st;
	//遍历操作序列,并入栈
	while(index != 0){st.push(tree[index].op);index = tree[index].pre;}
	while(!st.empty()){printf("%s", strs[st.top()]); st.pop();}
	printf("success\n");
}
int main(){
	int ca, cb, n;
	while(~scanf("%d%d%d", &ca, &cb, &n)){
		print(BFS(ca, cb, n));
	}
	return 0;
}

问题 B: DFS or BFS?

时间限制: 1 Sec  内存限制: 128 MB
提交: 552  解决: 113
[提交][状态][讨论版][命题人:外部导入]

题目描述

说好了,题目不黑人。

给你一个8*8的矩阵,你的初始位置是左下角方格(用'U’表示),你的目标位置是右上角的方格(用'A'表示),其余的62个方格,如果是'.',表示这个方格为空,如果是'S',表示这个方格有一块大石头。好了现在你开始从左下角出发,每次可以往上,下,左,右,左上,右上,左下,右下移动一个方格,或者你可以原地不动,一共九个动作方式,在你做完一个动作后,所有的大石头会往下掉一个方格(如果一个大石头的位置是(x,y),那下一秒是(x+1,y),不过如果它已经在最下面的一排了,那它就会掉出矩阵,不再出现),请注意,任一时刻,你不能和某一个大石头处在同一个方格,否则石头会把你XX掉。

现在的问题就是:你能从左下角安全抵达右上角么? 如果能,输出“Yes”,反之,“No”。

输入

T->测试数据组数(T)。

对于每组数据,输入一个8*8的矩阵,其后有一空行。描述如上。

输出

对于第i组数据,请输出

Case #i: s(s是一个字符串,如果可以到达,则s为“Yes”,反之“No”)

样例输入

<span style="color:#333333">2
.......A
........
........
........
........
........
........
U.......

.......A
........
........
........
........
.S......
S.......
US......
</span>

样例输出

<span style="color:#333333">Case #1: Yes
Case #2: No
</span>

[提交][状态]

AC代码及思路

AC代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<sstream>
using namespace std;
struct node{
    int x, y;
    int step;
};
const int maxn=10;
const int step_max=100;
const int m=8, n=8;
const int dx[]={0,-1,0,1,0,1,1,-1,-1};
const int dy[]={-1,0,1,0,0,1,-1,1,-1};
void drop(int maze[][maxn]){
    for(int i=m; i>=1; i--){
        for(int j=1; j<=n; j++){
            if(!maze[i][j]) {
                maze[i][j]=1;
                if(j<=m) maze[i+1][j]=0;
            }
        }
    }
}
int BFS(node s, node e, int maze[][maxn], int inq[][maxn][step_max]){
    //drop(maze);
    queue<node> q;
    q.push(s);
    inq[1][1][0]=1;
    int pre_step=0;
    do{
        //队首元素出队
        node now=q.front();
        q.pop();
        //石头下坠
        if(pre_step != now.step) drop(maze);
        pre_step=now.step;
        //判断是否撞墙
        if(maze[now.x][now.y]==0) continue;
        //判断是否到达终点。若是 则返回结束循环。
        if(now.x==e.x && now.y==e.y) return now.step;
        //遍历九种动作。可行动作的状态转移入队,反之跳过;
        for(int i=0; i<9; i++){
            node next={now.x+dx[i], now.y+dy[i], now.step+1};
            if(maze[next.x][next.y] && !inq[next.x][next.y][next.step]) {
                q.push(next);
                inq[next.x][next.y][next.step]=1;
            }
        }
    }while(!q.empty());
    return -1;
}
int main(){
    int p;
    while(~scanf("%d", &p)){
        getchar();
        //遍历每个测试用例
        for(int cnt=1; cnt<=p; cnt++){
            //初始化、置零,设置初始状态,统一初始状态
            node s, e;
            int maze[maxn][maxn]={0}, inq[maxn][maxn][step_max]={0};
            // memset(maze, 0, sizeof(maze));
            // memset(inq, 0, sizeof(inq));
            //格式控制,吸收xishou吸收吸收huanhangfu
            if(cnt!=1) {getchar();}
            //扫描迷宫,对迷宫进行编码
            for(int i=1; i<=m; i++){
                for(int j=1; j<=n; j++){
                    char c=getchar();
                    switch(c){
                        case 'U': s.x=i,s.y=j,maze[i][j]=1;break;
                        case 'A': e.x=i,e.y=j,maze[i][j]=1;break;
                        case '.': maze[i][j]=1;break;
                        case 'S': maze[i][j]=0;break;
                    }
                }
                getchar();
            }
            // //输出迷宫
            // for(int i=1; i<=m; i++){
            //     for(int j=1; j<=n; j++){
            //         cout<<maze[i][j]<<" ";
            //     }
            //     cout<<endl;
            // }
            //BFS搜索
            int re=BFS(s, e, maze, inq);
            //输出测试结果
            if(re==-1) printf("Case #%d: No\n", cnt);
            else printf("Case #%d: Yes\n", cnt);
            
        }
    }
    return 0;
}

问题 C: 【宽搜入门】8数码难题

时间限制: 20 Sec  内存限制: 128 MB
提交: 195  解决: 71
[提交][状态][讨论版][命题人:外部导入]

题目描述

初始状态的步数就算1,哈哈

输入:第一个3*3的矩阵是原始状态,第二个3*3的矩阵是目标状态。
输出:移动所用最少的步数

 

Input

2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5

Output

6

[提交][状态]

AC代码及思路

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
// char Table[400000000]={0};
map<string, int> mp;
const int maxn=4;
const int dx[]={0,-1,0,1};
const int dy[]={-1,0,1,0};
struct node{
    int x, y;
    int a[maxn][maxn];
    int step;
};
void copyM(int a[][maxn], int b[][maxn]){
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            a[i][j] = b[i][j];
        }
    }   
}
//散列映射,a[]映射为string
string hashM(int a[][maxn]){
    string str;
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            str.push_back(a[i][j]+'0');
        }
    }
    return str;
}
int findM(int a[][maxn]){
    
}
int equalM(int a[][maxn], int b[][maxn]){
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            if(a[i][j] != b[i][j]) return 0;
        }
    }
    return 1;
}
void printM(int a[][maxn]){
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}
int BFS(node s, int b[][maxn]){
    queue<node> q;
    q.push(s);
    do{
        node now = q.front();
        q.pop();
        if(equalM(now.a, b)) return now.step;
        for(int i=0; i<4; i++){
            //构造,搜索下一结点
            node next = now;
            next.x = now.x + dx[i];
            next.y = now.y + dy[i];
            if(next.x>3 || next.x<1 || next.y>3 || next.y<1) continue;
            swap(next.a[now.x][now.y], next.a[next.x][next.y]);
            next.step++;
            //判断结点是否入队过。用Table记录结点的入队信息
            if(mp.find(hashM(next.a)) == mp.end()) {
                q.push(next);
                mp[hashM(next.a)]=1;
            }
            // printf("%d\n", hashM(next.a));
            // printM(next.a);
        }
    } while(!q.empty());
    return -1;
}
int main(){
    int b[maxn][maxn]={0};
    node s;
    int x, y;
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            scanf("%d", &s.a[i][j]);
            if(s.a[i][j] == 0) s.x=i, s.y=j, s.step=1;
        }
    }
    for(int i=1; i<=3; i++){
        for(int j=1; j<=3; j++){
            scanf("%d", &b[i][j]);
        }
    }
    // printM(s.a);
    // printM(b);
    printf("%d\n", BFS(s, b));
    return 0;
}

问题 D: 【宽搜入门】魔板

时间限制: 1 Sec  内存限制: 128 MB
提交: 205  解决: 51
[提交][状态][讨论版][命题人:外部导入]

题目描述

在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
下面是对基本状态进行操作的示范:
A:
8 7 6 5
1 2 3 4
B:
4 1 2 3
5 8 7 6
C:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
【输入格式】
输入有多组测试数据
只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间),表示目标状态。
【输出格式】
Line 1: 包括一个整数,表示最短操作序列的长度。
Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7

BCABCCB 

[提交][状态]

AC代码及思路

AC代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<string>
#include<set>
using namespace std;
struct node{
    int cube[3][5];
    int pre_cube;
    int step;       
	string ops;     //记录操作序列
};
// 状态转移函数;
node action(node now, int act){
    switch(act){
        case 0: 
            for(int i=1; i<=4; i++){
                swap(now.cube[1][i], now.cube[2][i]);
            }
            break;
        case 1: 
            for(int i=4; i>=2; i--){
                swap(now.cube[1][i-1], now.cube[1][i]);
                swap(now.cube[2][i-1], now.cube[2][i]);
            }
            break;
        case 2: 
            swap(now.cube[1][2], now.cube[1][3]);
            swap(now.cube[1][2], now.cube[2][2]);
            swap(now.cube[2][2], now.cube[2][3]);
            break;
    }
    now.step ++;
    now.ops.push_back(act+'A');
	return now;
}
//二维数组的比较
int equalCube(int now[][5], int endC[][5]){
    for (int i=1; i<=2; i++){
        for(int j=1; j<=4; j++){
            if(now[i][j] != endC[i][j]) return 0;
        }
    }
    return 1;
}
// 二维数组的输出
void printCube(int a[][5]){
    for(int i=1; i<=2; i++){
		for(int j=1; j<=4; j++){
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
// 结点合法性与重复性的检验
int judgeC(node now, set<int>& st){
    int hash=0;
    int product=1;
    for(int i=1; i<=2; i++){
		for(int j=1; j<=4; j++){
			hash += (now.cube[i][j]-1)*product;
			product *= 8;
		}
	}
	if(st.find(hash) != st.end()) {
	    return 0;
	}
	else {
	    st.insert(hash);
	    return 1;
	}
}
//广度优先搜索
node BFS(node b, node e){
    set<int> st;    //记录结点入队信息;
    queue<node> q;  //等待阅兵的队列
    q.push(b);
    do{
        node now = q.front();
        q.pop();
        if( equalCube(now.cube, e.cube) ) return now;
        for(int i=0; i<3; i++){
            node next = action(now, i);
            if(judgeC(next, st)){
                q.push(next);
                //next.op = now.op + str;
            }
        }
    }while(!q.empty());
    return {0};
}
//主程序
int main(){
    node b;
    //初始化起点状态
    int beginCube[3][5]={{0},{0,1,2,3,4},{0,8,7,6,5}};
    for(int i=1; i<=2; i++){
        for(int j=1; j<=4; j++){
            b.cube[i][j] = beginCube[i][j];
        }
    }
    b.step = 0;
	b.ops="";
    //初始化目标状态
    node e;
    for(int i=1; i<=1; i++){
        for(int j=1; j<=4; j++){
            scanf("%d", &e.cube[i][j]);
        }
    }
    for(int i=2; i<=2; i++){
        for(int j=4; j>=1; j--){
            scanf("%d", &e.cube[i][j]);
        }
    }
    //搜索
    node ans = BFS(b, e);
    cout<<ans.step<<endl;
	cout<<ans.ops<<endl;
    return 0;
}

问题 E: 【宽搜入门】巧妙取量

时间限制: 2 Sec  内存限制: 128 MB
提交: 264  解决: 84
[提交][状态][讨论版][命题人:外部导入]

题目描述

【题目描述】
  有三个容器,容量分别为 a,b,c(a> b > c ),一开始a装满油,现在问是否只靠abc三个容器量出k升油。如果能就输出“yes”,并且说明最少倒几次,否则输出“no”。例如:10升油在10升的容器中,另有两个7升和3升的空容器,要求用这三个容器倒油,使得最后在abc三个容器中有一个刚好存有5升油,问最少的倒油次数是多少?(每次倒油,A容器倒到B容器,或者A内的油倒完,或者B容器倒满。
 10 7 3
(10 0 0)
(3 7 0) : 第一次
(3 4 3):第二次
(6 4 0):第三次
(6 1 3):第四次
(9 1 0):第五次
(9 0 1):第六次
(2 7 1):第七次
(2 5 3):第八次,出现5了。

Input

【输入格式】
  有多组测试数据。
  输入a,b,c, k四个正整数( 100 ≥ a > b > c≥1 , 1≤k< 100 )

Output

【输出格式】
  如果能得到k就输出两行。
  第一行“yes”,第二行为最少的次数
  否则输出“no”

Sample Input

10 7 3 5

Sample Output

yes
8

[提交][状态]

AC代码及思路

采用set来记录入队信息,较好地平衡了 查找效率与内存消耗。

AC代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<string>
#include<set>
using namespace std;
int k;
int c[3];
const int num_s=3;    // 状态的数量
const int num_a=6;    // 行动的数量
struct node{
    int state[num_s];
    int step;       
	string ops;     //记录操作序列
};
// a->b
void pour(int s[], int a, int b){ 
    if(s[a]+s[b]>c[b]){
        s[a] = s[a]+s[b]-c[b];
        s[b] = c[b];
    }else{
        s[b] = s[a]+s[b];
        s[a] = 0;
    }
}
// 状态转移函数;
node generate_NextState(node now, int action){
    switch(action){
        case 0:     //a->b
            pour(now.state, 0, 1);
            break;
        case 1:     //b->a
            pour(now.state, 1, 0);
            break;
        case 2:      //a->c
            pour(now.state, 0, 2);
            break;
        case 3:      //c->a
            pour(now.state, 2, 0);
            break;
        case 4:      //b->c
            pour(now.state, 1, 2);
            break;
        case 5:      //c->b
            pour(now.state, 2, 1);
            break;
    }
    now.step ++;
    now.ops.push_back(action +'0');
	return now;
}
//状态向量的比较
int equalState(int state[], int endS[]){
    for (int i=0; i<num_s; i++){
        if(state[i] != endS[i]) return 0;
    }
    return 1;
}
// 是否完成目标。当前状态与终止状态的比较
int reachEnd(int state[]){
    for (int i=0; i<3; i++){
        if(state[i] == k) return 1;
    }
    return 0;
}
// 状态向量的输出
void printState(int a[]){
    for(int i=0; i<num_s; i++){
		printf("%d ", a[i]);
	}
	printf("\n");
}
// 结点合法性与重复性的检验
int inq(node now, set<int>& st){
    int hash=0;
    int product=1;
    //构造对应的散列
    for(int i=0; i<num_s; i++){
		hash += now.state[i]* product;
		product *= 110;
	}
	if(st.find(hash) == st.end()) {
	    st.insert(hash);
	    return 0;
	}else return 1;
}
//广度优先搜索
int BFS(node b, node& ans){
    set<int> st;    //记录结点入队信息;
    queue<node> q;  //等待阅兵的队列
    q.push(b);
    inq(b, st);
    do{
        node now = q.front();
        q.pop();
        if( reachEnd(now.state) ) {
            ans = now;
            return now.step;
        }
        for(int i=0; i<num_a; i++){
            node next = generate_NextState(now, i);
            if( !inq(next, st) ){
                // printState(next.state);
                q.push(next);
            }
        }
    }while(!q.empty());
    return -1;
}
//主程序
int main(){
    while(~scanf("%d%d%d%d", &c[0], &c[1], &c[2], &k)){
        //初始化起点状态
        node b={0};
        b.state[0] = c[0];
        //初始化目标状态(终止状态)
        //检验
        // printState(b.state);
        // printState(e[0]);
        //搜索
        node ans;
        if(~BFS(b, ans)) cout<<"yes"<<endl<<ans.step<<endl;
        else cout<<"no"<<endl;
        // cout<<ans.ops<<endl;
        // string str = ans.ops;
        // for(int i=0; i<str.length(); i++){
        //     b = generate_NextState(b, str[i]-'0');
        //     printState(b.state);
        // }
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值