菠菜

敏感而豁达

分油问题

问题描述:

两个小孩去打油,一人带了一个一斤的空瓶,另一个带了一个七两和一个三两的空瓶。原计划各打一斤油,可是由于所带的钱不够,只好合打了一斤油,在回家的路上,二人想平分这一斤油,可是又没有其它工具。现只用这三个瓶子(一斤、七两、三两)精确地分出两个半斤油来。

问题分析:

分油问题的初始状态可表示为(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;
	}
}

posted on 2012-07-06 17:40  ~菠菜~  阅读(862)  评论(0编辑  收藏  举报

导航