HDU 1495 非常可乐

题目如下:

蕊蕊和翔翔合资买了一瓶肥宅快乐水儿,他们一共有两个杯子,加上可乐瓶一共有3个可以盛水的容器,并且两个杯子的容积的和等于可乐瓶的容积。蕊蕊和翔翔都不肯让对方喝到更多的肥宅快乐水儿。那么问题来了,在杯子和可乐瓶都没有刻度的情况下,是否有办法可以平分这瓶肥宅快乐水儿?

由于没有刻度,你只能进行两种操作,可以将一个容器中的可乐全部倒入另一个容器中,或者可以将一个容器中的可乐倒入另一个容器使其装满。

当某两个容器中的可乐都等于原可乐瓶容积的一半时,则认为此时可以平分。


Input

输入有多组数据。

每组数据三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。


Output

如果能平分的话请输出最少要倒的次数,否则输出"NO"。


Sample Input

7 4 3

4 1 3

0 0 0


Sample Output

NO

3


解题思路:

这道题跟POJ 3414这道题差不多,我们可以将三个容器之间相互倒可乐看成是遍历的六个方向(具体看代码)。并且在这道题中S如果为奇数,就直接输出NO。(因为S为奇数的话则不能进行平分!)并且,这道题中三个容器之间都可以互相倒可乐,所以我们需要开一个三维数组来记录一下各个容器当中水的状态。(例如:S容器为7,N容器为4,M容器为3的话,那么点就为(7,4,3)。如果这个点没有访问过则将这个点设置为yes并添加到队列中,否则就继续搜索其它状态)由于这道题还需要输出最少的操作次数,因此这道题我们采用bfs记忆化搜索。(详情参考POH 3414 这道题的笔记,这里就不再过多讲解!)


POJ 3414 笔记如下: https://www.cnblogs.com/gao79135/p/14088520.html


在这道题中,由于S的大小在(0,101)之间,因此S的下标为1-100,且由于N+M==100,所以N和M容器的下标都为1-99。所以我们设置访问数组的大小为:status[104][104][104],并且队列的大小也应该在100x100x100之下。(因为实际遍历的状态中远小于这个数)。


代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct Point
{
	int Swater;               //S可乐的容量
	int Nwater;               //N可乐的容量
	int Mwater;               //M可乐的容量
	int pre;                  //定义当前状态中之前的状态在队列中的下标。
} Q[250000];                  
bool status[104][104][104];     //代表标记该状态是否被访问过的数组
int Count = 0;                //定义最少倒的次数
int S, N, M;                  //代表可乐的体积,两个杯子的容量
int flag = 0;
struct Point bfs();           //代表进行bfs的函数
void dfs(struct Point end_point);                   //代表进行递归的函数(这里用dfs表示)
void init();                                        //代表进行初始化的函数

void init()
{
	int i, j, k;
	for (i = 0; i < 104; i++)
	{
		for (j = 0; j < 104; j++)
		{
			for (k = 0; k < 104; k++)
			{
				status[i][j][k] = false;
			}
		}
	}
}

struct Point bfs()
{
	int moves, moven, movem;  //代表移动之后的容器状态
	int i, now=0, front=1; 
	int mid = S / 2;
	Q[0].Swater = S;          //定义刚开始的状态
	Q[0].Mwater = 0;
	Q[0].Nwater = 0;
	Q[0].pre = -1;
	status[S][0][0] = true;   //代表起点已经访问过
	while (true)
	{
		if (now > front)
		{
			break;
		}
		for (i = 0; i < 6; i++)
		{
			moves = Q[now].Swater;
			moven = Q[now].Nwater;
			movem = Q[now].Mwater;
			if (i == 0)         //代表将S容器的水倒入N容器
			{
				int pour = min(moves, N - moven);
				moves -= pour;
				moven += pour;
				if (pour == 0)  //如果N容器的水已满,代表S容器的水无法倒入N容器,结果为状态跟之前一样没有改变,所以需要进行返回
				{
					continue;
				}
			}
			else if (i == 1)   //代表将S容器的水倒入M容器
			{
				int pour = min(moves, M - movem);
				moves -= pour;
				movem += pour;
				if (pour == 0)
				{
					continue;
				}
			}
			else if (i == 2)  //代表将N容器的水倒入S容器
			{
				int pour = min(moven, S - moves);
				moven -= pour;
				moves += pour;
				if (pour == 0)
				{
					continue;
				}
			}
			else if (i == 3) //代表将N容器的水倒入M容器
			{
				int pour = min(moven, M - movem);
				moven -= pour;
				movem += pour;
				if (pour == 0)
				{
					continue;
				}
			}
			else if (i == 4) //代表将M容器的水倒入S容器
			{
				int pour = min(movem, S - moves);
				movem -= pour;
				moves += pour;
				if (pour == 0)
				{
					continue;
				}
			}
			else if (i == 5) //代表将M容器的水倒入N容器
			{
				int pour = min(movem, N - moven);
				movem -= pour;
				moven += pour;
				if (pour == 0)
				{
					continue;
				}
			}
			if (status[moves][moven][movem] == false)  //如果该状态还没有访问过
			{
				front++;
				Q[front].Swater = moves;
				Q[front].Nwater = moven;
				Q[front].Mwater = movem;
				Q[front].pre = now;
				status[moves][moven][movem] = true;   //代表该状态已经访问过了
			}
		}
		if ((Q[now].Swater == mid && Q[now].Nwater == mid) || (Q[now].Swater == mid && Q[now].Mwater == mid) || (Q[now].Nwater == mid && Q[now].Mwater == mid))
		{
			return Q[now];                        //返回当前状态
		}
		now++;
	}
	struct Point bad;
	bad.pre = -10000;
	return bad;
}

void dfs(struct Point end_point)
{
	if (end_point.pre == -10000)
	{
		cout << "NO" << endl;
		flag = 1;
		return;
	}
	if (end_point.pre != -1)
	{
		Count++;
	}
	if (end_point.pre == -1)
	{
		return;
	}
	dfs(Q[end_point.pre]);                        //代表从当前状态递归到该状态之前的状态
}

int main()
{
	struct Point end_point;
	while (scanf("%d %d %d", &S, &N, &M) != EOF)
	{
		init();
		if (S == 0 && N == 0 && M == 0)
		{
			return 0;
		}
		else
		{
			if (S % 2 == 0)
			{
				end_point = bfs();
				dfs(end_point);
				if (flag == 0)
				{
					cout << Count << endl;
				}
			}
			else
			{
				cout << "NO" << endl;
			}
		}
		Count = 0;
		flag = 0;
	}
}
上一篇:Pset_BuildingElementProxyCommon


下一篇:Pset_WallCommon