倒水问题 BFS+记忆化搜索

假定两个无刻度有容量水壶 A A A B B B,供水量不限。可以使用三种方法装水:

给一个水壶装水;
把一个水壶倒空;
从一个水壶倒进另一个水壶。

当从一个水壶倒进另一个水壶时,如果第一个水壶倒空,或者第二个水壶装满就不能再倒了。例如,一个水壶 A A A是5加仑和另一个水壶 B B B是6加仑,水量是8加仑,则从水壶 A A A倒进水壶B时,让水壶 B B B充满水而水壶 A A A剩3加仑水。
问题由3个参数: C a Ca Ca​, C b Cb Cb​, N N N,分别表示水壶 A A A B B B的容量,目标水量 N N N。解决问题的目标是给出一系列倒水的步骤,使水壶中的水量恰好是 N N N

本题是由一道有名的数学题的一般形式。对于这个问题,最基本的思路肯定是需要利用两个桶反复取水倒水,最后得到目标值。题目要求步骤最少且写出步骤,容易想到使用BFS来求解问题。接下来考虑状态转移。
因为水壶无刻度有容量,这意味着从水井取水但不把水壶装满是无意义的。所以有六个转移方向:
1. 1. 1.Fill A A A
2. 2. 2.Fill B B B
3. 3. 3.Empty A A A
4. 4. 4.Empty B B B
5. 5. 5.pour A B AB AB,用 A A A中水倒到 B B B中。
6. 6. 6.pour B A BA BA,用 B B B中水倒到 A A A中。
每达到一个状态,就执行 6 6 6种操作,并压入队列,记下这个状态后继续搜索。直到出现解后停止搜索并输出。
然而,因为每次都会有6个状态入队,所以说重复的做法有非常多。这是我们就需要记忆化搜索来进行简化搜索。我们使用一个二维数组 c h e c k check check来记录 A , B A,B A,B里有若干水所需要的步骤数,步骤数最小的才入队,简化步骤。

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int a,b;
    int num;
    string s="";
};
int k,A,B,ANS;    
int check[1001][1001];
void bfs()
{
//	cout<<"1"<<endl;
    queue<node>q;
    node x;
    memset(check,999999,sizeof(check));
    x.a=0;x.b=0;x.num=0;
    q.push(x);
    while(!q.empty())
    {
        node y=q.front();q.pop();
        for(int i=1;i<=6;i++)
        {
            node z=y;
            z.num++;
            if(i==1){z.a=A;z.s=z.s+"1";}
            if(i==2){z.b=B;z.s=z.s+"2";}
            if(i==3){z.a=0;z.s=z.s+"3";}
            if(i==4){z.b=0;z.s=z.s+"4";}
            if(i==5)
            {
                if(z.b+z.a>A)
                {
                    z.b=z.b-(A-z.a);
                    z.a=A;
                }
                else 
                {
                    z.a=z.b+z.a;
                    z.b=0;
                }
                z.s=z.s+"5";   
            }
            if(i==6)
            {
                if(z.b+z.a>B)
                {
                    z.a=z.a-(B-z.b);
                    z.b=B;
                }
                else 
                {
                    z.b=z.a+z.b;
                    z.a=0;
                }
                z.s=z.s+"6";
            }
            if(check[z.a][z.b]>z.num)
			{
				check[z.a][z.b]=z.num;
				q.push(z);
			}
           // cout<<z.b<<endl;
			if(z.b==ANS)
            {
                cout<<z.num<<" ";
    			for(int i=0;i<z.s.size();i++)cout<<z.s[i]<<" ";cout<<endl;
    			return ;
            }
        }
    }
}
int main()
{
    cin>>k;
    for(int i=1;i<=k;i++)
    {
        cin>>A>>B>>ANS;
        bfs();
    }
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值