hdu 1495 非常可乐 BFS+倒水问题

传送门:[](http://acm.hdu.edu.cn/showproblem.php?pid=1495hdu 1495 非常可乐)
题目是中文的没有什么理解问题

解题思路

先来把问题特殊化,假如我们有三个容器分别容量是6升,3升还有一升,现在我们把六升的容器装满水,问经过多少次之后能到达给的4升。这个我们很容易通过简单地思考就能知道答案,过程为:
(6,0,0)->(3,3,0)->(3,2,1)->(4,3,0);
然后我们要解决的问题就变为了:有三个容器大中小三种类型,在最开始的时候只有最大的杯子装满了水,经过n次倒水之后将其中一个容器中的水变为给定d升。
由于我们要找的是最小的步骤,那么可以用BFS,来找出最小的步骤。
那么问题来了,应该怎么进行枚举呢?
因为其中两个杯子确定了那么第三个杯子的剩余量就确定了,我们只需要记录其中的两个杯子的状态就可以了,我们通过vis[i][j]这个数组中,i表示中等容量的容器现有水量,j表示最小容量的容器的现有水量。
然后我们只需要遍历某个容器向另一个容器倒水就可以了。这一共六种状态。用两个for循环就能解决的。倒水过程代码如下:

 for(int i=0;i<3;i++)
        {
            for(int j=0;j<3;j++)
            {
                if(i == j) //不能自己向自己倒水
                    continue;
                //下面想不通可举个数据自己尝试一下,cur.v[i]表示第i个杯子的水的剩余量
                //cup_capacity[j]-cur.v[j]表示第j个杯子可以接收的最大水量,
                //倒水之后必须是两种状态中的其中一种,第i个杯子剩余的水为空
                //第j个杯子水中满了,但是第i个杯子不一定为空
                int amount = min(cur.v[i],cup_capacity[j]-cur.v[j]); // 取出可以倒向第j个杯子的水
                for(int k=0;k<3;k++)
                    next.v[k] = cur.v[k];
                //第i个杯子倒向第j个杯子
                next.v[i] = cur.v[i] - amount;
                next.v[j] = cur.v[j] + amount;
                //如果这个状态没有被访问过,就把这个状态压入队列
                if(!vis[next.v[1]][next.v[2]])
                {
                    vis[next.v[1]][next.v[2]] = true;
                    next.deepth = cur.deepth + 1;
                    q.push(next);
                }
            }
        }

对应于这个题目来讲,因为S = M + N所以M或者N中必然会有一个数会大于S/2;随后如果要平分的话肯定要max(M,N)中的水量等于S这个容器中的水量并且等于S/2;
这样就抽象为了上面的倒水问题:

#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
int cup_capacity[4];
bool vis[105][105];
struct Node{
    int v[3];
    int deepth;
};

int bfs()
{
    if(cup_capacity[0]%2)
        return -1;
    queue<Node>q;
    Node cur;
    Node next;
    cur.v[0] = cup_capacity[0];
    cur.v[1] = 0;
    cur.v[2] = 0;
    cur.deepth = 0;
    vis[0][0] = true;
    q.push(cur);
    while(!q.empty())
    {
        cur = q.front();
        q.pop();
        if(cur.v[0] == cur.v[1] && cur.v[2] == 0)
            return cur.deepth;
        for(int i=0;i<3;i++)
        {
            for(int j=0;j<3;j++)
            {
                if(i == j) //不能自己向自己倒水
                    continue;
                //下面想不通可举个数据自己尝试一下,cur.v[i]表示第i个杯子的水的剩余量
                //cup_capacity[j]-cur.v[j]表示第j个杯子可以接收的最大水量,
                //倒水之后必须是两种状态中的其中一种,第i个杯子剩余的水为空
                //第j个杯子水中满了,但是第i个杯子不一定为空
                int amount = min(cur.v[i],cup_capacity[j]-cur.v[j]); // 取出可以倒向第j个杯子的水
                for(int k=0;k<3;k++)
                    next.v[k] = cur.v[k];
                //第i个杯子倒向第j个杯子
                next.v[i] = cur.v[i] - amount;
                next.v[j] = cur.v[j] + amount;
                //如果这个状态没有被访问过,就把这个状态压入队列
                if(!vis[next.v[1]][next.v[2]])
                {
                    vis[next.v[1]][next.v[2]] = true;
                    next.deepth = cur.deepth + 1;
                    q.push(next);
                }
            }
        }

    }
    return -1;
}
int main()
{
    while(scanf("%d%d%d",&cup_capacity[0],&cup_capacity[1],&cup_capacity[2])!=EOF)
    {
        memset(vis,false,sizeof vis);
        if(!cup_capacity[0] && !cup_capacity[1] && !cup_capacity[2])
            break;
        int tmp1 = cup_capacity[1];
        int tmp2 = cup_capacity[2];
        cup_capacity[1] = max(tmp1,tmp2);
        cup_capacity[2] = min(tmp1,tmp2);
        /*if(cup_capacity[0]%2)
            printf("NO\n");
        else if(cup_capacity[1] == cup_capacity[2])
            printf("1\n");
        else
            printf("3\n");*/
        int ans = bfs();
        if(ans == -1)
            printf("NO\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值