Loading

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();
    }
}
posted @ 2022-12-23 17:51  DM11  阅读(48)  评论(0编辑  收藏  举报