Q:有A B两个杯子,A杯子 7 L,B杯子 9 L,如何得到 1 L水?
A:使用BFS或者DFS 边建立图,边搜索 有装 1 L水的杯子 的状态。(实际上该问题为多叉树结构,BFS类似于层次遍历,DFS类似于先续遍历,在遍历过程中建立 树 ,到有装 1 L水的杯子 的节点停止。)
给出代码及注释:
/**************************************************************************
PourWater.h
**************************************************************************/
#pragma once
#include "stdafx.h"
#include "iostream"
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
//动作 初始化, A倒进B, B倒进A, 倒满A, 倒满B, 倒空A, 倒空B
char* const VActions[7] = { "INIT", "A_TO_B","B_TO_A","FILL_A","FILL_B","EMPTY_A","EMPTY_B"};
struct state //两杯子的状态
{
int A, B; //俩杯子容积
int a, b; //俩杯子中剩余的水
state* parent; //指向上一个状态
char* action; //刚刚执行的动作
state(int _A, int _B) : A(_A), B(_B), a(0), b(0), parent(NULL), action(VActions[0]) {}
state(int _a, int _b, state* _parent, char* _action) : A(_parent->A), B(_parent->B), a(_a), b(_b), parent(_parent), action(_action) {}
state* nextState(int i) { //执行某一动作后的状态, 如果检测到重复状态返回NULL
if (1 == i) return checkPred(this, a - MIN(B - b, a), b + MIN(B - b, a), VActions[i]);
if (2 == i) return checkPred(this, a + MIN(A - a, b), b - MIN(A - a, b), VActions[i]);
if (3 == i) return checkPred(this, A, b, VActions[i]);
if (4 == i) return checkPred(this, a, B, VActions[i]);
if (5 == i) return checkPred(this, 0, b, VActions[i]);
if (6 == i) return checkPred(this, a, 0, VActions[i]);
return 0;
}
state* checkPred(state* S, int _a, int _b, char* _action) { //执行某一动作后,其所有祖先不能出现过相同的状态,否则会死循环
do
if (S->a == _a && S->b == _b) return 0;
while (S = S->parent);
return new state(_a, _b, this, _action);
}
void backTrace(state* S) { //打印以S为叶节点的 可行路径
if(state* _S = S->parent) backTrace(_S);
std::cout << "(A:" << S->a << ", B:" << S->b << ") " << S->action << std::endl;
}
};
// A, B 为杯子容积, D 为目标水量
void pour(int A, int B, int D, state* S){
state* V = NULL;
for (int i = 1; i < 7; i++) { //从S开始,可能1-6种状态(INIT状态只在最初始设置)
if (S->a == D || S->b == D) { S->backTrace(S); std::cout << std::endl; break; }
if (V = S->nextState(i)) pour(A, B, D, V);
}
}
void pourWater(int A, int B, int D) {
state* S = new state(A, B); //初始状态INIT
pour(A, B, D, S);
}
/**************************************************************************
main
**************************************************************************/
int main()
{
pourWater(7, 9, 1);
}
给出可行解之一:
上述方法基于DFS(仅仅考虑节点祖先的状态,而不考虑其他节点状态),给出所有不重复状态条件下的所有解;所以这些解是相对于祖先不重复状态的最优解。
若要考虑所有情况下,不重状态的最优解,则需要基于BFS构造图(引入队列,层次遍历)。基于DFS的解 包含 基于BFS的解。