[kuangbin带你飞]专题一 简单搜索:G - Pots

链接POJ 3414—Pots

题意
输入三个整数A,B,C(A,B<=100并且C<=max(A,B))。
A,B分别代表两个水杯的最大容积。

一开始两个水杯都是空的,你可以进行如下6个操作(题目中说是3种,将所有操作扩展之后是6个操作):

FILL(1):将第1个杯子装满。

FILL(2):将第2个杯子装满。

DROP(1):倒掉第1个杯子里的水。

DROP(2):倒掉第2个杯子里的水。

POUR(1,2):将第1个杯子里的水倒向第2个杯子。

POUR(2,1):将第2个杯子里的水倒向第1个杯子。

你可以反复执行上述操作。

问最少经过多少次操作后两个杯子中至少有一个杯子的水量等于C?

输出最少操作次数并输出具体操作。(具体看样例)

思路
一个非常有趣的题目,和N - 迷宫问题是同一类型的搜索题。

在这里插入图片描述

比较难的点还是在于如何找到此步操作的上一步的状态与操作

首先对于最少操作这个点我们可以先进行一个剪枝,将一些无意义的操作无视掉。

1.12)号杯已经满了,我们不执行12)操作。

2.12)号杯已经空了,我们不执行34)操作。

3.12)号杯已经满了,我们不执行65)操作。

4.12)号杯已经空了,我们不执行56)操作。

简而言之,就是不向已经满了的杯子倒水(来源无所谓)不让空杯向外面倒水(倒在哪里无所谓)

其次就是当我们执行第5种操作与第6种操作时出现的两种情况:

POUR(1,2):将第1个杯子里的水倒向第2个杯子。

POUR(2,1):将第2个杯子里的水倒向第1个杯子。

以5操作举例:当我们将1号杯倒向2号杯时,我们需要考虑:

(1)1号杯中水量大于2号杯中剩余容量时(此时min(v1,b-v2)=b-v2)。

(2)1号杯中水量不大于2号杯中剩余容量时(此时min(v1,b-v2)=v1)。

其中v1代表1号杯中水量,v2代表2号杯中水量,b代表2号杯容量。
那么如果是(1)状态,那么操作之后就应该是

v1 = v1-b+v2 = v1-min(v1 ,b-v2)

v2 = v2+b-v2 = v2+min(v1 ,b-v2)

如果是(2)状态,那么操作之后就是:

v1 = v1 - v1 = v1-min(v1 ,b-v2)

v2 = v2 + v1 = v2+min(v1 ,b-v2)

所以我们得到执行过5操作后v1,v2的表达式:

v1=v1-min(v1 ,b-v2)

v2=v2+min(v1 ,b-v2)

同理,执行6操作之后v1,v2的表达式就应该是(a为1号杯的容量):

v1=v1+min(v2 ,a-v1)

v2=v2-min(v2 ,a-v1)

讲完这些细节我们来看一下主要的部分:如何知道这一步用了哪个操作,又如何知道我从哪一步用了哪个操作才来到了这一步?

首先我们先定义一个int的二维数组:

//maxn=105
int done[maxn][maxn];

done[i][j]的值的含义:当1号杯水量为i,2号杯水量为j时,我们上一步做了哪种操作

比如样例中初始状态为0 0。
我们执行操作2,状态变为0 5,那么done[0][5]=2。

接下来定义结构体node:

struct node
{
    int x,y;         //x:当前1号杯的水量  y:当前2号杯的水量
    node() {}
    node(int xx,int yy):x(xx),y(yy) {}
};

done数组只能帮我们存储操作方式,却无法记录当前状态是从哪个状态转化而来,此时我们创建一个结构体数组from:

struct node
{
    int x,y;
    node() {}
    node(int xx,int yy):x(xx),y(yy) {}
} from[maxn][maxn];

from[i][j]与done[i][j]之间的关系:1号杯水量为x,2号杯水量为y的①号状态,通过done[i][j]的操作之后,变为1号杯水量为i,2号杯水量为j的②号状态。

理解了这两个数组的意义之后接下来A掉这道题就像如履平川一般。

直接放代码,「伊丽莎白」!
在这里插入图片描述
代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <stdio.h>
#include <stack>
#include <queue>
using namespace std;
const int maxn=105;
struct node
{
    int x,y;
    node() {}
    node(int xx,int yy):x(xx),y(yy) {}
} from[maxn][maxn];
int done[maxn][maxn];
bool vis[maxn][maxn];        //标记数组
int a,b,c,k;
stack<int>s;                 //用栈存储每一步的操作,两次逆序后顺序就正常了
void BFS()
{
    queue<node>q;
    memset(vis,false,sizeof(vis));
    q.push(node(0,0));
    vis[0][0]=true;
    while(!q.empty())
    {
        node tmp=q.front();
        q.pop();
        if(tmp.x==c||tmp.y==c)     //两者之中只要有一者达到C即可
        {
            while(tmp.x!=0||tmp.y!=0)    //只要两个杯子不全为0,那么一定有前置状态以及对应的转化操作
            {
                s.push(done[tmp.x][tmp.y]);
                k=tmp.x;
                tmp.x=from[tmp.x][tmp.y].x;
                tmp.y=from[k][tmp.y].y;    //这个地方由于tmp.x已经变成上一步的状态,所以先设置变量保存当前x的状态
            }
            cout<<s.size()<<endl;
            while(!s.empty())              //对应序号输出对应内容
            {
                if(s.top()==1)
                    cout<<"FILL(1)"<<endl;
                else if(s.top()==2)
                    cout<<"FILL(2)"<<endl;
                else if(s.top()==3)
                    cout<<"DROP(1)"<<endl;
                else if(s.top()==4)
                    cout<<"DROP(2)"<<endl;
                else if(s.top()==5)
                    cout<<"POUR(1,2)"<<endl;
                else
                    cout<<"POUR(2,1)"<<endl;
                s.pop();
            }
            return;
        }
        node tail;
        if(tmp.x<a&&!vis[a][tmp.y])        //1号操作
        {
            tail.x=a;
            tail.y=tmp.y;
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=1;
            q.push(tail);
        }
        if(tmp.y<b&&!vis[tmp.x][b])       //2号操作
        {
            tail.x=tmp.x;
            tail.y=b;
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=2;
            q.push(tail);
        }
        if(tmp.x>0&&!vis[0][tmp.y])       //3号操作
        {
            tail.x=0;
            tail.y=tmp.y;
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=3;
            q.push(tail);
        } 
        if(tmp.y>0&&!vis[tmp.x][0])      //4号操作
        {
            tail.x=tmp.x;
            tail.y=0;
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=4;
            q.push(tail);
        }
        if(tmp.x>0&&tmp.y<b&&!vis[tmp.x-min(tmp.x,b-tmp.y)][tmp.y+min(tmp.x,b-tmp.y)])  //5号操作
        {
            tail.x=tmp.x-min(tmp.x,b-tmp.y);
            tail.y=tmp.y+min(tmp.x,b-tmp.y);
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=5;
            q.push(tail);
        }
        if(tmp.x<a&&tmp.y>0&&!vis[tmp.x+min(tmp.y,a-tmp.x)][tmp.y-min(tmp.y,a-tmp.x)])  //6号操作
        {
            tail.x=tmp.x+min(tmp.y,a-tmp.x);
            tail.y=tmp.y-min(tmp.y,a-tmp.x);
            vis[tail.x][tail.y]=true;
            from[tail.x][tail.y].x=tmp.x;
            from[tail.x][tail.y].y=tmp.y;
            done[tail.x][tail.y]=6;
            q.push(tail);
        }
    }
    cout<<"impossible"<<endl;        //如果无法到达最终状态输出impossible
    return;
}
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>a>>b>>c;
    BFS();
    return 0;
}

这个题也是思考了快一周才想出来,主要是要想出来from和done数组以及其意义。
开心开心~~

下次再见~
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值