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;
}
}
标签:容器,HDU,movem,moven,int,pour,1495,now,可乐 来源: https://www.cnblogs.com/gao79135/p/14090331.html