kuangbin带你飞——专题一 简单搜索(8)
题目来源:POJ 3414 Pots
题解
一种倒水问题,通过对两个瓶子每次进行一种操作接水(使A装满水),倒水(将A的水倒空),从A向B中倒水(在不浪费水的情况下,能倒多少倒多少)
,使得一个瓶子中的水量为目标水量。
输出最快花费时间(BFS搜索时间)
和路径(DFS回溯路径搭配数组BFS)
。
主要运用记忆化搜索
。
题目重点
超出边界(i>=cnt)
时,跳出循环不可到达目标水量
。
和之前的数据相同时,不进行存储。
AC代码
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
using namespace std;
// ta,tb 表示此时 A 与 B 中的水量,pre 表示上一个点的下标,opr 表示操作。
struct status
{
int ta, tb;
int pre, opr;
};
// 作为 dfs 中记录回溯次数的标记 == 操作次数
int length = 0;
// A 与 B 的最大水量,c 表示目标水量
int m, n, c;
// 操作的六个方向
string out[6] = {"FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"};
status s[10000];
// BFS 搜索
status bfs()
{
// 起点
s[0].ta = s[0].tb = 0,
s[0].pre = s[0].opr = -1;
int cnt = 1;
for (int i = 0;; ++i)
{
// 超出边界
if (i >= cnt)
break;
// 遍历六个方向
for (int j = 0; j < 6; ++j)
{
// tmp 的初始化
status tmp = s[i];
tmp.pre = i, tmp.opr = j;
// 操作进行,并判断是否存储操作
if (j == 0)
{
if (tmp.ta == m)
continue;
tmp.ta = m;
}
else if (j == 1)
{
if (tmp.tb == n)
continue;
tmp.tb = n;
}
else if (j == 2)
{
if (tmp.ta == 0)
continue;
tmp.ta = 0;
}
else if (j == 3)
{
if (tmp.tb == 0)
continue;
tmp.tb = 0;
}
else if (j == 4)
{
int pour = min(tmp.ta, n - tmp.tb);
if (pour == 0)
continue;
tmp.ta -= pour, tmp.tb += pour;
}
else
{
int pour = min(tmp.tb, m - tmp.ta);
if (pour == 0)
continue;
tmp.tb -= pour, tmp.ta += pour;
}
// 到达终点
if (tmp.ta == c || tmp.tb == c)
return tmp;
// 判断是否有重复的情况
bool flag = 1;
for (int k = 0; k < cnt; ++k)
{
if (s[k].ta == tmp.ta && s[k].tb == tmp.tb)
{
flag = 0;
break;
}
}
if (flag)
s[cnt++] = tmp;
}
}
// 返回不可能的情况
s[0].pre = -100;
return s[0];
}
// 回溯输出结果
void dfs(status now)
{
if (now.pre == -1)
{
cout << length << endl;
return;
}
// 标志的叠加
++length;
dfs(s[now.pre]);
cout << out[now.opr] << endl;
}
int main()
{
scanf("%d%d%d", &m, &n, &c);
status ans = bfs();
if (ans.pre != -100)
{
dfs(ans);
}
else
{
printf("impossible\n");
}
return 0;
}