倒水问题(两个杯子)

一.题目描述
现在拥有两个杯子,容量分别为a和b,要求使用这两个杯子量出c容量的水,并输出两个杯子所做的操作。
详细题目如下:
在这里插入图片描述
二.思路概述
使用bfs的思路。两个水杯初始状态都是0,而每一次都可以做六种不同的操作,这些操作会导致两个水杯变成不同的状态,再将这些状态入队列,再次进入循环,直到两个水杯中有一个里面的水量为c。在上述过程中,可使用map数组来记录每一个状态的前状态,便于回溯。

三.各部分作用
1.结构体pour:
a,b分别对应着两个杯子里的水量,操作符重载是为了map函数,六个函数对应着六种状态。

struct pour
{
	int a;
	int b;//两个杯子里的水量 
	pour(){}
	pour(int x,int y)
	{
		a=x;
		b=y;
	}
	//操作符重载 
	pour& operator=(pour& val)
	{
		a=val.a;
		b=val.b;	
		return *this;
	}
    
    bool operator!=(const pour &va) const {
    return a!= va.a|| b != va.b ;
    }
    bool operator<(const pour &va) const {
     return a == va.a ? b < va.b : a < va.a;
    }
	//六种操作对应的函数 
	pour fillA()
	{
		return pour(A,b);
	} 
	pour fillB()
	{
		return pour(a,B);
	}
	pour emptyA()
	{
		return pour(0,b);
	}
	pour emptyB()
	{
		return pour(a,0);
	}
	pour AtoB()
	{
		pour c;
		c.a=max(a+b-B,0);
		c.b=min(a+b,B);
		return c;
	} 
	pour BtoA()
	{
		pour c;
		c.a=min(a+b,A);
		c.b=max(a+b-A,0);
		return c;
	} 
};

2.check函数
用来给每一次改变状态后水量入队列和记录前驱状态

void check(pour m,pour n)//用来记录前一个状态 
{
    if(mps.find(m)  == mps.end())
    {
	sp.push(m);
	mps[m]=n; 
	}	
}

3.print部分
根据最后的状态进行回溯,并将这些状态入栈,然后用栈从初状态开始输出结果。

void print(pour t)//pp的a或者b==C 
{	
    
	while(!(t.a==0&&t.b==0))
	{	
	xp.push(t) ;
	pour va=mps[t];
	t=va;
	}
    xp.push(t); 

	while(xp.size()>1 )
	{
		pour m=xp.top() ;
		xp.pop();
		pour n=xp.top() ;
		determine(m,n);
		
	} 
	xp.pop() ;
	
}

四.注意的问题
1.由于此题可能会做不止一次输入和输出,所以map数组、栈、队列都要注意是否清空,栈和队列没有直接清空的函数,需要手动进行。
2.map函数使用起来很方便,解决了很多问题,但是map函数的使用规则可能不是那么友好,比如在check函数中一定要第一行,确认关键字没有出现过,才插入,不然就会报错。

if(mps.find(m)  == mps.end())
    {
	sp.push(m);
	mps[m]=n; 
	}

3.在回溯这个过程中,我尝试过使用数组和栈来存储进入队列的状态,但是都失败了,这两种方法都不好掌握,结果惨烈,最后使用了map。

五.完整代码

#include<iostream>
#include<map>
#include<queue>
#include<stack> 
using namespace std;

int A,B,C;
int nu=0;


struct pour
{
	int a;
	int b;//两个杯子里的水 
	pour(){}
	pour(int x,int y)
	{
		a=x;
		b=y;
	}
	pour& operator=(pour& val)
	{
		a=val.a;
		b=val.b;
		return *this;
	}

    bool operator!=(const pour &va) const {
    return a!= va.a|| b != va.b ;
    }
    bool operator<(const pour &va) const {
     return a == va.a ? b < va.b : a < va.a;
    }
	//六种操作对应的函数 
	pour fillA()
	{
		return pour(A,b);
	} 
	pour fillB()
	{
		return pour(a,B);
	}
	pour emptyA()
	{
		return pour(0,b);
	}
	pour emptyB()
	{
		return pour(a,0);
	}
	pour AtoB()
	{
		pour c;
		c.a=max(a+b-B,0);
		c.b=min(a+b,B);
		return c;
	} 
	pour BtoA()
	{
		pour c;
		c.a=min(a+b,A);
		c.b=max(a+b-A,0);
		return c;
	} 
};

queue<pour> sp;//队列存储每次状态
stack<pour> xp;//栈 
map<pour,pour> mps;


void check(pour m,pour n)//用来记录前一个状态 
{

    if(mps.find(m)  == mps.end())
    {
	sp.push(m);
	mps[m]=n; 
	}
	
}
void determine(pour m,pour n)
{
	if(n.a==A&&n.b==m.b)
    cout<<"fill A"<<endl;
	else if(n.b==B&&n.a==m.a)
	 cout<<"fill B"<<endl;
	else if(n.a==0&&n.b==m.b)
	 cout<<"empty A"<<endl;
	else if(n.b==0&&n.a==m.a)
	 cout<<"empty B"<<endl;
	else if((n.a==0&&n.b==m.a+m.b)||(n.b==B&&n.a==m.a+m.b-B))
	 cout<<"pour A B"<<endl;
	else if((n.b==0&&n.a==m.a+m.b)||(n.a==A&&n.b==m.a+m.b-A))
	 cout<<"pour B A"<<endl;
}

void print(pour t)//pp的a或者b==C 
{	
    
	while(!(t.a==0&&t.b==0))
	{	
	xp.push(t) ;
	pour va=mps[t];
	t=va;
	}
    xp.push(t); 

	while(xp.size()>1 )
	{
		pour m=xp.top() ;
		xp.pop();
		pour n=xp.top() ;
		determine(m,n);
		
	} 
	xp.pop() ;
	
}
void bfs(pour t)
{
	//从0,0开始为初始状态
	mps[t]=t; 
	sp.push(t);
	
	while (!sp.empty()) 
	{		
        t = sp.front();
        sp.pop();
        if (t.a == C ||t.b == C) //跳出循环,输出步骤 
		{
		    //cout<<111<<endl;
            print(t);
            while(!sp.empty() )
            sp.pop() ;
            return;
        }
        //每次做完六种操作的状态进队列,直到x和y的值有一个为c停止
        check(t.AtoB(), t);
        check(t.BtoA(), t);
        check(t.fillA(), t);
        check(t.fillB(), t);
        check(t.emptyA(), t);
        check(t.emptyB(), t);
		//cout<<nu<<endl;		
    }
	
	//记录每次状态的前一个操作,最后输出所有的操作
	 
}
int main()
{
	
	while(cin>>A)
	{
	     cin>>B>>C;
		 pour p(0,0);//初始状态, 
		 bfs(p);
		 cout<<"success"<<endl;
		 mps.clear() ;
		 	 
	}
	return 0;
} 
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值