分油问题
问题描述:
两个小孩去打油,一人带了一个一斤的空瓶,另一个带了一个七两和一个三两的空瓶。原计划各打一斤油,可是由于所带的钱不够,只好合打了一斤油,在回家的路上,二人想平分这一斤油,可是又没有其它工具。现只用这三个瓶子(一斤、七两、三两)精确地分出两个半斤油来。
问题分析:
分油问题的初始状态可表示为(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; } }