[隐式图]Exercise Week2 B-Pour Water

题意

经典逻辑题 倒水问题-有A,B两个容器,问如何倒出C体积的水
“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
输入:0 < A <= B 、C <= B <=1000 、A和B互质。
输出要求:每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
会有多组数据(保证数据能够成功倒出C体积的水)


样例

样例输入:
2 7 5
2 7 4
样例输出:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success


思路

可以把两个容器当前水的量当做一种状态(即BFS中 图的一个节点),状态之间的转移只有 1.将A倒空 2.将A加满 3.将B倒空 4.将B加满 5.将A倒入B 6.将B倒入A六种可能(相当于BFS中上下左右的走)。因此我们只需从最初(0,0)的状态开始执行BFS,每次从队列取出一个状态,然后将其6个扩展的状态都进行判断是否到达过,到达过则记录状态的前驱 以及将它放入队列,直到状态变成(C,~ )或( ~,C).然后通过map中记录的前驱来输出方案。
在方案的输出方面,可以根据一个状态的前驱到该状态的特性,来判断是怎么进行的(比如 s–>t 如果 t.a= = s.a&& t.b= = 0 则s到t是进行了 "empty B"操作)


总结

1.状态抽象:找到可以唯一表示一个中间状态的方法,可以用sturct构造状态
2.搜索抽象:找到所有可以由一个状态扩展成另一个状态的途径,并且可以完整的表示出来
3.按照一般的BFS进行搜索


代码

#include<iostream>
#include<math.h.>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
struct node
{
    int a;int b;
    bool operator <(const node &s)  const
    {
        return a!=s.a?a<s.a:b<s.b;
    }
    bool operator ==(const node &s)  const
    {
        return a==s.a&&b==s.b;
    }
    bool operator !=(const node &s)  const
    {
        return a!=s.a||b!=s.b;
    }
}pre,final;
queue<node> q;
map<node,node> from;//map映射 from[i]=j 表示 状态i从状态j过来 即 j-->i
void check(node & x)
{
    if(from.find(x)==from.end())//find()函数用来寻找是否已存在x 满足if则不存在
    {
        from[x]=pre;//pre用来记录前驱
        q.push(x); 
    }
}
int A,B,C;
void pour_water()
{
    node s,t;
    s.a=0;s.b=0;
    q.push(s);
    from[s]=s;
    while(!q.empty())
    {
        s=q.front();q.pop();
        //终点判断条件
        if(s.a==C||s.b==C)  {final=s;return;}

        //对所有状态的遍历
        if(s.a>0) //把a里的水倒空
        {
            t.a=0;  t.b=s.b;    pre=s;
            check(t);
        }
        
        if(s.b>0)//倒空b
        {
            t.b=0;  t.a=s.a;    pre=s;
            check(t);
        }
        
        if(s.a!=A)//把a倒满
        {
            t.a=A;  t.b=s.b;    pre=s;
            check(t);
        }

        if(s.b!=B)//把b倒满
        {
            t.b=B;  t.a=s.a;    pre=s;
            check(t);
        }
        
        if(s.a!=0&&s.b!=B)//a倒入b
        {
            if(s.a+s.b<=B)
            {
                t.a=0;  t.b=s.a+s.b;    pre=s;
                check(t);
            }
            else 
            {
                t.a=s.a+s.b-B;  t.b=B;  pre=s;
                check(t);
            }
        }

        if(s.a!=A&&s.b!=0)//b倒入a
        {
            if(s.a+s.b<=A)
            {
                t.a=s.a+s.b;    t.b=0;  pre=s;
                check(t);
            }
            else
            {
                t.a=A;  t.b=s.a+s.b-A;  pre=s;
                check(t);
            }
        }
    }
}
void print()
{
    vector<node> v;
    for(;from[final]!=final;final=from[final])    v.push_back(final);
    v.push_back(final);//把最初状态也放进去

    node s,t;
    for(int i=v.size()-1;i>0;i--)
    {
        s=v[i];t=v[i-1];//状态顺序为s-->t
        //倒空A
        if(t.a==0&&t.b==s.b)    cout<<"empty A"<<endl;
        //倒空B
        if(t.b==0&&t.a==s.a)    cout<<"empty B"<<endl;
        //倒满A
        if(t.a==A&&t.b==s.b)    cout<<"fill A"<<endl;
        //倒满B
        if(t.b==B&&t.a==s.a)    cout<<"fill B"<<endl;
        //a倒入b
        if(t.a+t.b==s.a+s.b&&(t.b==B||t.b==s.a+s.b))    cout<<"pour A B"<<endl;
        //b倒入a
        if(t.a+t.b==s.a+s.b&&(t.a==A||t.a==s.a+s.b))    cout<<"pour B A"<<endl;
    }
    cout<<"success"<<endl;
}
int main()
{
    while(cin>>A>>B>>C)
    {
        //每次要把队列和map清空
        while(!q.empty())   q.pop();
        from.clear();
        pour_water();
        print();
    }

    system("pause");
    return 0;
}
发布了12 篇原创文章 · 获赞 0 · 访问量 522

猜你喜欢

转载自blog.csdn.net/linshen_jianhai/article/details/104562620
今日推荐