题解 分油问题

分油

题目描述

有3个油瓶X、Y、Z容量分别为A斤、B斤、C斤,开始时3个瓶的油量分别为A1、B1、C1,编程输出最少倒多少步,才可以使3个瓶的油量分别为A2、B2、C2。每次倒油以目标瓶满或者原始瓶空为结束。

输入描述

输入共3行,分别为:
A B C {三个瓶的最大容量,假设100>A>B>C>=1}
A1 B1 C1 {初始状态}
A2 B2 C2 {目标状态}

输出描述

一行一个数,表示最少倒多少步。保证有解。

样例

输入

50 17 9
50 0 0
25 17 8

输出

5

做题思路

因为要求最少倒多少步,所以这道题用宽搜的方法来做。首先我们要知道倒油的规则,倒油的规则有六种:
1.从X桶倒到Y桶;
2.从X桶倒到Z桶;
3.从Y桶倒到X桶;
4.从Y桶倒到Z桶;
5.从Z桶倒到X桶;
6.从Z桶倒到Y桶;
在倒油前,要判断当前桶是否为空且要倒的桶是否为满,如果不是,就进行倒油操作。
但是如果分别写这六个规则,会特别麻烦,代码也特别长。所以我们可以用for循环简化。
如果当前桶的油加上要倒的桶的油超过要倒的桶的容量,那么要倒的桶倒满,当前桶还剩下两个桶的总油量减去要倒的桶的容量;
否则当前桶倒空,另一个桶的油量是它本身的油量加上当前桶的容量。
倒油之后,要记录三个桶的状态。
如果这个状态以前出现过了,那么直接跳过,因为出现过的状态再出现一次肯定就不是最短路径了。
最后输出最少倒的步数。
但是标记数组二维数组不够用,可以用三维数组。你别以为只有一维二维数组,还有多维数组。不过多维数组不要开太大,否则会内存超限。

代码

#include<iostream>
#include<queue>
using namespace std;
int a1,b1,c1,a2,b2,c2,m[4];//定义
struct node{
    int x,y,z;
    int step;
};
bool vis[102][102][102];//定义访问数组
void fun(int a,int b,int c){//宽度优先搜索函数
    queue<node>q;
    node ttmp;
    ttmp.x=a,ttmp.y=b,ttmp.z=c,ttmp.step=0;
    q.push(ttmp);//入队
    vis[a][b][c]=1;
    while(!q.empty()){//在队列不为空的情况下操作
        node tmp=q.front();
        q.pop();//出队
        int aa[4],bb[4];
        aa[1]=tmp.x,aa[2]=tmp.y,aa[3]=tmp.z;
        if(aa[1]==a2&&aa[2]==b2&&aa[3]==c2){//判断三个桶是否都是目标状态
            cout<<tmp.step<<endl;//输出最少倒多少步
            return;//函数结束
        }
        for(int i=1;i<=3;i++){//这里把六个规则简化成循环,变成从i桶倒到j桶
            for(int j=1;j<=3;j++){
                if(i==j){
                    continue;
                }
                bb[1]=aa[1],bb[2]=aa[2],bb[3]=aa[3];
                if(aa[i]>0&&aa[j]<m[j]){//判断能否倒油
                    if(aa[i]+aa[j]>=m[j]){//判断两个桶的总油量是否比要倒的桶的容量大
                        bb[j]=m[j];//要倒的桶的油量灌满
                        bb[i]=aa[i]+aa[j]-m[j];
                    }
                    else{
                        bb[i]=0;//一个桶空
                        bb[j]=aa[i]+aa[j];//另一个的油量是这两个桶的油量之和
                    }
                }
                if(!vis[bb[i]][bb[2]][bb[3]]){//判断状态是否重复
                    ttmp.x=bb[1],ttmp.y=bb[2],ttmp.z=bb[3];
                    ttmp.step=tmp.step+1;//步数增加
                    q.push(ttmp);//进队列
                }
            }
        }
    }
}
int main(){
    cin>>m[1]>>m[2]>>m[3];//输入
    cin>>a1>>b1>>c1>>a2>>b2>>c2;
    fun(a1,b1,c1);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值