问题描述:
两个小孩去打油,一人带了一个一斤的空瓶,另一个带了一个七两和一个三两的空瓶。原计划各打一斤油,可是由于所带的钱不够,只好合打了一斤油,在回家的路上,二人想平分这一斤油,可是又没有其它工具。现只用这三个瓶子(一斤、七两、三两)精确地分出两个半斤油来。
问题分析:
分油问题的初始状态可表示为(10,0,0),然后经过一系列的循环倒油,直到所求的目标状态(5,5,0)。
由于没有其它工具,因此这里只有两种基本倒油操作:倒空源瓶或倒满目标瓶。倒油过程中,若目标瓶已达到容量极限或倒油操作导致的状态之前曾经出现过,则此次倒油是没有必要的。
源代码:
#include <iostream>
#include <vector>
#include <array>
using namespace std;
#define State array<int, 3>
#define Step array<int, 2>
// 设置每个瓶子最大容量
State MaxOil{ 10, 7, 3 };
// 存放每个历史步骤,仅供查看,可不用
//vector<Step> Steps;
// 存放每个瓶子历史容量
vector<State> History;
// “倒油”操作
// fromIndex:源油瓶索引
//toIndex:目标油瓶索引
//oilStatus:油状态
void SwichOil(int fromIndex, int toIndex, State& oilStatus)
{
int toMax = MaxOil[toIndex] - oilStatus[toIndex];
int from = oilStatus[fromIndex];
if (from >= toMax)
{
oilStatus[fromIndex] = oilStatus[fromIndex] - toMax;
oilStatus[toIndex] = MaxOil[toIndex];
}
else
{
oilStatus[toIndex] += oilStatus[fromIndex];
oilStatus[fromIndex] = 0;
}
}
// 对比两个油的状态是否一样
bool Compare(State& oil1, State& oil2)
{
for (int i = 0; i < 3; i++)
{
if (oil1[i] != oil2[i])
{
return false;
}
}
return true;
}
// 倒油步骤的算法,返回新的步骤
//oil:倒油前的油瓶状态
//lastStep:上一个步骤
Step* GetNextStep(State &oil, Step* lastStep)
{
//新步骤
Step *step = NULL;
//把油瓶状态做一个备份
State recordOil(oil);
//循环源油瓶索引
for (int from = 0; from < 3; from++)
{
if (oil[from] == 0)
{
//如果源油瓶为0,则continue
continue;
}
//循环目标油瓶索引
for (int to = 0; to < 3; to++)
{
//是否找到步骤
bool isFindStep = true;
if (from == to)
{
//源与目标相同的话,略过
continue;
}
if (lastStep != NULL && (*lastStep)[1] == from && (*lastStep)[0] == to)
{
//步骤与上一次步骤lastStep是反操作的,略过
continue;
}
if (oil[to] == MaxOil[to])
{
//目标油瓶容量已达到极限,掠过
continue;
}
//“倒油”
SwichOil(from, to, oil);
//查看新状态是否存在历史存在过
for(int i=0; i< History.size(); i++)
{
if (Compare(History[i], oil))
{
//如果有,则跳出并复原
isFindStep = false;
oil = recordOil;//复原
break;
}
}
//如果已找寻到步骤
if (isFindStep)
{
//记录步骤,以及记录新油瓶容量状态
step = new Step; (*step)[0] = from, (*step)[1] = to;
State newOil;
newOil = oil;
History.push_back(newOil);
break;
}
}
//如果找寻到步骤,就跳出循环
if (step != NULL)
{
// Steps.push_back(*step);
break;
}
}
return step;
}
void ShowState(State &st)
{
cout << "(" << st[0] << " " << st[1] << " " << st[2] << ")";
}
void ShowStep(Step &sp)
{
cout << sp[0] << "-->" << sp[1];
}
int main()
{
State st{10,0,0};
History.push_back(st); // 这一步是必须的,不然程序会出错
cout << "init: "; ShowState(st); cout << endl;
Step *pNewStep = NULL;
bool cont = true;
while (cont)
{
pNewStep = GetNextStep(st, pNewStep);
if(History.back()[0] == 5 && History.back()[1] == 5)
{
cont = false;
}
if (pNewStep == NULL)
{
cont = false;
}
ShowStep(*pNewStep); cout << " "; ShowState(st); cout << endl;
}
}