poj3414 Pots (bfs加回溯)

题意:

给出两个水壶的容量A,B, 和一个目标水壶容量C,

对水壶可以有三种操作:

1、FILL(i)        fill the pot i (1 ≤ i ≤ 2) from the tap;

把水壶(1或2)装满

2、DROP(i)      empty the pot i to the drain;

把水壶(1或2)全部倒了

3、POUR(i,j)    pour from pot i to pot j; after this operation either the pot j is full (and there may be some water left in the pot i), or the pot i is empty (and all its contents have been moved to the pot j).

把水壶 i 的水全部倒倒 水壶 j 中

问:

如果经过几个操作可以使任意一个瓶子的残余水量为C,则输出多少个操作,哪几个操作

如果不可能得到残余水量为C,则输出impossible

 

解题思路:

问题1:搜索什么时候应该停止

A,B,C的取值范围是1~100,因此可以使用一个二维数组来表示水壶的状态

map[ i ][ j ]: 表示水壶1有 i 升水,水壶2有 j 升水的状态,根据题意有(A+1)*(B+1)种状态, 最多10201种状态。

当(A+1)*(B+1)种状态都搜索过或搜索到目标容量C状态时,搜索应该停止

从当前状态map[ i ][ j ]出发一共有6种方法:

方法1:装满水壶1   转移状态到map[ A ][ j ]

方法2:倒空水壶1   转移状态到map[ 0 ][ j ]

方法3:水壶1的水倒入水壶2

方法4:装满水壶2   转移状态到map[ i ][ B ]

方法5:倒空水壶2   转移状态到map[ i ][ 0 ]

方法6:水壶2的水倒入水壶1

问题2:如何回溯

map[ i ][ j ]: 表示水壶1有 i 升水,水壶2有 j 升水的状态。在这个二维数组中,需要存储4个信息:表示前一个状态时两个水壶装了多少水,前一个状态到当前状态时采用了哪种方法(数字表示,对应前面的6种方法的排序),到当前状态一共执行了几步操作。

typedef struct
{
    int prefirst;//表示前一个状态时,水壶1装了多少水
    int presecond;//表示前一个状态时,水壶2装了多少水
    int preOper;//前一个状态到当前状态时采用了哪种方法(数字表示,对应前面的6种方法)
    int steps;//到当前状态一共执行了几步操作。-1表示没有搜索过。
}node;
node map[110][110];

搜索到 水壶1或水壶2为C时,记录当前状态,开始回溯,操作被逆序存放在recordOper数组种

void  backSteps(int x, int y)
{
    int i=0;
    int preX, preY;
    while(x!=0 || y!=0)
    {
        recordOper[i] = map[x][y].preOper;
        preX = map[x][y].prefirst;
        preY = map[x][y].presecond;
        x = preX;
        y = preY;
        i++;
    }
}

 

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int A, B, C;
int recordOper[10000];

typedef struct
{
    int first;//水壶1装了多少水
    int second;//水壶2装了多少水
}pos;

typedef struct
{
    int prefirst;//表示前一个状态时,水壶1装了多少水
    int presecond;//表示前一个状态时,水壶2装了多少水
    int preOper;//前一个状态到当前状态时采用了哪种方法(数字表示,对应前面的6种方法)
    int steps;//到当前状态一共执行了几步操作。-1表示没有搜索过。
}node;
node map[110][110];

void operation(int op, pos &next, pos &tmp)
{
    if(op == 1)//FILL(第一个)  fill the pot i (1 ≤ i ≤ 2) from the tap;
    {
        next.first = A;
        next.second = tmp.second;
    }
    if(op == 2)//DROP(第一个)      empty the pot i to the drain;
    {
        next.first = 0;
        next.second = tmp.second;
    }
    if(op == 3)//POUR(1,2) 把第一个往第二个倒,需要判断水壶2的水会不会满
    {
        if(tmp.first+tmp.second >= B)
        {
            next. first = tmp.first+tmp.second - B;
            next.second = B;
        }
        else
        {
            next.first = 0;
            next.second = tmp.first+tmp.second;
        }
    }
    if(op == 4)//FILL(第二个)  fill the pot i (1 ≤ i ≤ 2) from the tap;
    {
        next.first = tmp.first;
        next.second = B;
    }
    if(op == 5)//DROP(第二个)      empty the pot i to the drain;
    {
        next.first = tmp.first;;
        next.second = 0;
    }
    if(op ==6)//POUR(2,1) 把第二个往第一个倒,需要判断水壶1的水会不会满
    {
        if(tmp.first+tmp.second >= A)
        {
            next.first = A;
            next.second = tmp.first+tmp.second - A;
        }
        else
        {
            next.first = tmp.first+tmp.second;
            next.second = 0;
        }
    }
}

void  backSteps(int x, int y)
{
    int i=0;
    int preX, preY;
    while(x!=0 || y!=0)
    {
        recordOper[i] = map[x][y].preOper;
        preX = map[x][y].prefirst;
        preY = map[x][y].presecond;
        x = preX;
        y = preY;
        i++;
    }
}

int dfs()
{
    int ret = -1;
    pos tmp,next;
    queue<pos> q;
    q.push(pos{0,0});

    while(q.size())
    {
        tmp = q.front();
        q.pop();
        if(tmp.first==C || tmp.second==C)//
        {
            ret = map[tmp.first][tmp.second].steps;
            backSteps(tmp.first, tmp.second);
            break;
        }
        for (int i = 1; i < 7; ++i)
        {
            operation(i, next, tmp);
            if(map[next.first][next.second].steps == -1)
            {
                map[next.first][next.second].prefirst = tmp.first;
                map[next.first][next.second].presecond = tmp.second;
                map[next.first][next.second].preOper = i;
                map[next.first][next.second].steps = map[tmp.first][tmp.second].steps + 1;
                q.push(next);
            }
        }
    }

    while(q.size())
        q.pop();

    return ret;
}

int main()
{
    int ans;
    while(~scanf("%d %d %d", &A, &B, &C))
    {
        for(int i = 0; i <= A; i++)
        {
            for(int j = 0; j <= B; j++)
            {
                map[i][j].steps = -1;
            }
        }
        map[0][0].steps = 0;//忘了初始化,贡献了一发WA
        
        ans = dfs();//ans是操作数,如果ans=-1,说明时不可能的。
        if(ans == -1)
            printf("impossible\n");
        else
        {   //如果ans不等于-1,说明可行,并且具体操作被逆序存放在recordOper
            printf("%d\n", ans);
            for(int i = ans-1; i>=0; i--)
            {
                if(recordOper[i] == 1)
                    printf("FILL(1)\n");
                if(recordOper[i] == 2)
                    printf("DROP(1)\n");
                if(recordOper[i] == 3)
                    printf("POUR(1,2)\n");
                if(recordOper[i] == 4)
                    printf("FILL(2)\n");
                if(recordOper[i] == 5)
                    printf("DROP(2)\n");
                if(recordOper[i] == 6)
                    printf("POUR(2,1)\n");
            }
        }
    }
    return 0;
}

注:有觉得解释不清的地方,可以留言,我再继续改进一下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值