状态空间搜索就是将问题求解过程表现为从初始状态到目标状态寻找路径的过程。
普通的搜索是在显式图中进行,从一个节点搜索到其他节点。
状态空间搜索是在一个由问题状态构成的隐式图中进行,从一个状态搜索到另一个状态。
状态空间搜索和普通搜索的区别无非是,这里搜索的不再是一个个实实在在的节点,而是一个个问题的状态,不过二者思路和方法都是相同的。
本题问题的状态可以用四个值来描述,第一个杯子的水量s,第二个杯子的水量n,第三个杯子的水量m,初始状态到当前状态的距离d。
从当前状态怎么搜索到其他状态呢?
假设第一个杯子的容量为6,第二个杯子的容量为4,第三个杯子的容量为2。
当前状态为 s = 6, n = 0, m = 0, d = 0
这时候我们可以将第一个杯子的水倒在第二个杯子里,这样状态就变成了s = 2, n = 4, m = 0, d = 1(因为进行了一步操作,所以d要加1)。
我们也可以将第一个杯子的水倒在第三个杯子里,这样状态就变成了s = 4, n = 0, m = 2, d = 1。
也就是(6, 0, 0, 0)这个状态可以搜索到(2, 4, 0, 1)和(4, 0, 2, 1)这两个新的状态(在隐含图中也就是在它们之间连边)。
这里因为n和m都为0,所以不能往其它杯子里倒水。但是我们要明白一个状态最多可以搜索到6个新的状态,也就是这六个操作:s->n, s->m, n->m, n->s, m->s, m->n (x->y表示x杯子往y杯子倒水)。
剩下的就按照bfs的基本操作写就好了,代码如下:
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int vis[200][200]; // 注意:这里只需要两维标记即可,因为已知两个杯子的水量,可以推算出第三个杯子的情况。
// 如果用三维标记会MLE。
struct Node
{
int s, n, m;
int d;
Node(int a, int b, int c, int e) : s(a), n(b), m(c), d(e) {}
};
int S, N, M;
// 判断是否满足结束条件,即有两个杯子的水量都是S / 2
bool is_goal(Node a)
{
int cnt = 0;
if (a.s == S / 2) cnt++;
if (a.n == S / 2) cnt++;
if (a.m == S / 2) cnt++;
if (cnt == 2) return true;
return false;
}
void bfs()
{
memset(vis, 0, sizeof(vis));
queue<Node> Q;
Node src(S, 0, 0, 0);
Q.push(src);
vis[S][0] = 1;
int flag = 0; // 标记是否找到了解
while (!Q.empty())
{
Node cur = Q.front();
Q.pop();
if (is_goal(cur))
{
printf("%d\n", cur.d);
flag = 1;
break;
}
int &s = cur.s, &n = cur.n, &m = cur.m, &d = cur.d;
// s->n
if (s && n != N)
{
int t1, t2;
if (s <= N - n)
t1 = 0, t2 = s + n;
else
t1 = s - N + n, t2 = N;
if (!vis[t1][t2])
vis[t1][t2] = 1, Q.push(Node(t1, t2, m, d + 1));
}
// s->m
if (s && m != M)
{
int t1, t2;
if (s <= M - m)
t1 = 0, t2 = s + m;
else
t1 = s - M + m, t2 = M;
if (!vis[t1][n])
vis[t1][n] = 1, Q.push(Node(t1, n, t2, d + 1));
}
// n->m
if (n && m != M)
{
int t1, t2;
if (n <= M - m)
t1 = 0, t2 = n + m;
else
t1 = n - M + m, t2 = M;
if (!vis[s][t1])
vis[s][t1] = 1, Q.push(Node(s, t1, t2, d + 1));
}
// n->s
if (n && s != S)
{
int t1, t2;
if (n <= S - s)
t1 = 0, t2 = n + s;
else
t1 = n - S + s, t2 = S;
if (!vis[t2][t1])
vis[t2][t1] = 1, Q.push(Node(t2, t1, m, d + 1));
}
// m->s
if (m && s != S)
{
int t1, t2;
if (m <= S - s)
t1 = 0, t2 = m + s;
else
t1 = m - S + s, t2 = S;
if (!vis[t2][n])
vis[t2][n] = 1, Q.push(Node(t2, n, t1, d + 1));
}
// m->n
if (m && n != N)
{
int t1, t2;
if (m <= N - n)
t1 = 0, t2 = m + n;
else
t1 = m - N + n, t2 = N;
if (!vis[s][t2])
vis[s][t2] = 1, Q.push(Node(s, t2, t1, d + 1));
}
}
if (!flag)
printf("NO\n");
}
int main()
{
//freopen("1.txt", "r", stdin);
while (cin >> S >> N >> M && S && N && M)
{
if (S & 1)
{
printf("NO\n");
continue;
}
bfs();
}
return 0;
}
本文通过一个具体的例子介绍状态空间搜索的概念及实现方法,详细解释了如何使用广度优先搜索(BFS)解决三个不同容量杯子的问题,以达到特定的目标状态。
1132

被折叠的 条评论
为什么被折叠?



