分油
题目描述
有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;
}