HDU 1495 非常可乐
HDU 1495 非常可乐
有一壶 S 毫升的酒,酒壶容量也是 S 毫升(没有刻度),现在有两个 N 毫升和 M 毫升的酒杯(也都没有刻度),\(S=N+M\),\(0 \le S,N,M \le 101\),这三只容器均可以相互倒酒,请问通过这三只容器之间相互倒酒,能否平分这 S 毫升的酒至两个酒杯之中?若能够平分的话,输出一行为最小的倒酒次数;若无法平分,输出一行NO。
思路:
数据比较小,先往暴力的方向想。因为有小次数,也就是等效于最短路问题,可以想到是搜索。
搜索的过程中有一个比较重要的就是转移。这一题的状态设计是需要做一些思考的。我们直接用3个容器当前的可乐的量来做状态,每次转移都是某个杯子往某个杯子里倒水。因为酒杯和酒壶都没有刻度,所以每次倒的时候会发生两种情况中至少一种:倒入的杯子满了或是倒出的杯子空了。根据这个,就可以模拟出倒水的过程了。
因为BFS是基于队列来拓展的一个过程,所以第一次出现在队列中一定是最小的倒酒次数。
代码:
记得判重。不要把起点漏掉了。
#include <bits/stdc++.h>
using namespace std;
int a, b, c;
int w[3];
int st[110][110][110]; //判重
struct node
{
int v[3];//三个杯子当前的状态
int step;
};
void bfs()
{
memset(st, 0, sizeof st);
queue<node> q;
q.push({w[0], 0, 0, 0}); //初始状态
st[w[0]][0][0] = 1;
while(q.size())
{
node t = q.front();
q.pop();
if(t.v[0] == t.v[2] && t.v[1] == 0) //平分时
{
cout << t.step << '\n';
return;
}
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(i != j)
{
node temp = t;
int minn = min(temp.v[i], w[j] - temp.v[j]); //从i倒水到j
// 倒入的杯子满了或是倒出的杯子空了
temp.v[i] -= minn, temp.v[j] += minn;
if(!st[temp.v[0]][temp.v[1]][temp.v[2]])
{
temp.step ++;
q.push(temp);
st[temp.v[0]][temp.v[1]][temp.v[2]] = 1;
}
}
}
}
}
cout << "NO" << '\n';
}
int main()
{
while(cin >> w[0] >> w[1] >> w[2])
{
if(w[0] == 0 && w[1] == 0 && w[2] == 0)
break;
if(w[1] > w[2]) //默认杯2的容量大于等于杯1,降低编码难度
swap(w[1], w[2]);
if(w[0] % 2 == 1) //可行性剪枝,易知若可乐总数是奇数,无法平分
cout << "NO" << '\n';
else
bfs();
}
}