POJ 3414 pots 详解(记录路径+非常可乐+最快路径模板)

21 篇文章 1 订阅


Pots
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 14918 Accepted: 6270 Special Judge

Description

You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:

  1. FILL(i)        fill the pot i (1 ≤ ≤ 2) from the tap;
  2. DROP(i)      empty the pot i to the drain;
  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).

Write a program to find the shortest possible sequence of these operations that will yield exactly C liters of water in one of the pots.

Input

On the first and only line are the numbers AB, and C. These are all integers in the range from 1 to 100 and C≤max(A,B).

Output

The first line of the output must contain the length of the sequence of operations K. The following K lines must each describe one operation. If there are several sequences of minimal length, output any one of them. If the desired result can’t be achieved, the first and only line of the file must contain the word ‘impossible’.

Sample Input

3 5 4

Sample Output

6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)

Source

Northeastern Europe 2002, Western Subregion


题意:给出两个容积分别为 a 和 b 的pot,按照以下三种操作方式,求出能否在一定步数后,使者两个pot的其中一个的水量为c。

      1.FILL(i):将ipot倒满水。

      2.DROP(i):将ipot倒空水。

      3.POUR(i,j): 将ipot的水倒到jpot上,直至要么ipot为空,要么jpot为满。

思路:这一题是结合了HDU 1495 非常可乐跟poj 3984 外加一个最短步数,记录路径需要一个id记录每一个数的id,一个pre记录他的上一步是从哪里来的。

先给poj 3984代码:主要写了记录路径的方式

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<stack>
using namespace std;
const int maxn = 10;
int pic[maxn][maxn];
bool book[maxn][maxn];
struct node
{
    int x, y, id, pre;     //用这个结构体记录每个数”id“跟pre(他的”父节点“)
    node(int xx, int yy, int ii, int pp): x(xx), y(yy), id(ii), pre(pp) {}
    node() {}
}a[maxn*maxn];
void bfs(void)
{
    int k = 1, next[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
    queue<node> q;
    q.push(node(0, 0, 1, 0));   //让第一个数的id = 1,pre是,用pre=0来结束循环调价,小细节。
    a[k++] = node(0, 0, 1, 0);  //用一个结构体数组记录每个节点的信息,他的下标跟每个数的id是一样的
    book[0][0] = 1;   //细节,一定让a的下标等于每个结构体的id
    node t;
    while(q.size())
    {
        t = q.front(); q.pop();
        if(t.x == 4 && t.y == 4) break;
        for(int i = 0; i < 4; i++)
        {
            int tx = t.x+next[i][0];
            int ty = t.y+next[i][1];
            if(tx >= 0 && ty >= 0 && tx < 5 && ty < 5 && !book[tx][ty] && !pic[tx][ty])
            {
                a[k++] = node(tx, ty, k, t.id);   //精髓,t是从队列出来的,所以这个for循环里面出的点都是从这个
                q.push(a[k-1]); //出,所以这个点就是他们的“父节点”,也就是pre都等于t.id
                book[tx][ty] = 1;
            }
        }
    }
    stack<node> s;
    while(1)   //如果某个t达到那个点,就把他push进一个栈,因为数组a记录了每个点的信息,而且下标就是每个点的id
    {           //所以从到达终点的点开始,往前找他的父节点,一直找到起点,就一定是最短的正确的路径
        s.push(t);
        if(!t.pre) break;
        t = a[t.pre];
    }
    while(!s.empty())
        printf("(%d, %d)\n", s.top().x, s.top().y), s.pop();
}
int main(void)
{
    for(int i = 0; i < 5; i++)
        for(int j = 0; j < 5; j++)
            scanf("%d", &pic[i][j]);
    bfs();
    return 0;
}

这个是非常可乐的题解: http://blog.csdn.net/qq_34374664/article/details/51646060

最后这一题的题解:

解题思路:
6入口的bfs

把两个两个桶的某个同一时间的状态看做一个整体,视为初态

可对初态进行六种操作,即FILL(1),FILL(2),DROP(1),DROP(2),POUR(1,2),POUR(2,1)

这6种操作在我的程序中分别用一个整数进行记录;

1z0: 清空z瓶子

2z0: 装满z瓶子

3xy: 从x瓶倒向y瓶

(x,y,z∈{1,2})

这样在输出操作时就能根据数字的特点 进行选择性输出 了

 

 

这题有两个难点:

第一就是要输出得到容量C的过程。

对于BFS的题而言这种要求是非常刁钻的,回溯每一步时很容易就混淆了下标。

解决方法:对于每一步的操作,我们都得对它设置一个pos指针,使它指向前一步操作在queue[]队列的下标,即对下标进行回溯,再顺序输出每一步操作

 

第二就是状态记录。怎样记录两个瓶子在同一时间点的状态,又能使得搜索标记时为O(1)的复杂度?

瓶子某时刻的残余水量就是该瓶子的状态,设残余水量为k1 k2

开始时我想对k1 k2进行各种运算组合,但都无法保证k1 k2的运算组合是独一无二的。

最后我决定用 ostringstream 在k1 k2 两个数字之间加上一个逗号,把他们变为字符串string,再利用map进行标记,相当成功

 

 

最后我简单说说各种操作的数学表达式:

设1瓶的容量为v1,残余水量为k1, 2瓶的容量为v2,残余水量为 k2

那么 fill(1) 相当于 k1=v1  fill(2)相当于k2=v2  drop(1)相当于k1=0   drop(2)相当于k2=0

对于Pour(1,2),若k1+k2<=v2,则 k2=k1+k2 , k1=0  (有先后顺序)

               否则k1=k1+k2-v2  , k2=v2  (有先后顺序)

Pour(2,1)亦同理



#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;
const int maxn = 1005;
struct node
{
    int a, b, step, pre, cmd, pos; //step 就是id, pre代表前一个,cmd代表他的行动,pos代表最短几步
    node(){}
    node(int aa, int bb, int ss, int pp, int cc, int p1) : a(aa), b(bb), step(ss), pre(pp), cmd(cc), pos(p1){}
}a[maxn*maxn];
int A, B, C, book[maxn][maxn], flag, k;
void bfs()
{

    queue<node> q;
    q.push(node(0, 0, -1, -1, -1, 0));
    book[0][0] = 1;
    flag = 0;
    k = 1;
    node p;
    while(!q.empty())
    {
        p = q.front();
        q.pop();
        if(p.a == C || p.b == C)
            {
                flag = 1;
                break;
            }
        if(p.a != A && !book[A][p.b])  //这里开始6中操作同时进行
        {
            a[k] = node(A, p.b, k, p.step, 1, p.pos+1);
            book[A][p.b] = 1;
            q.push(a[k]);
            k++;
        }
        if(p.b != B && !book[p.a][B])
        {
            a[k] = node(p.a, B, k, p.step, 2, p.pos+1);
            book[p.a][B] =  1;
            q.push(a[k]);
            k++;
        }
        if(p.a && !book[0][p.b])
        {
            a[k] = node(0, p.b, k, p.step, 3, p.pos+1);
            book[0][p.b] = 1;
            q.push(a[k]);
            k++;
        }
        if(p.b && !book[p.a][0])
        {
            a[k] = node(p.a, 0, k, p.step, 4, p.pos+1);
            book[p.a][0] = 1;
            q.push(a[k]);
            k++;
        }
        if(p.a != A && p.b)
        {
            int x = min(A, p.a+p.b);
            int y = max(0, p.b-A+p.a);
            if(!book[x][y])
            {
                a[k] = node(x, y, k, p.step, 5, p.pos+1);
                q.push(a[k]);
                book[x][y] = 1;
                k++;
            }

        }
        if(p.b != B && p.a)
        {
            int x = min(B,p.a+p.b);
            int y = max(0, p.a-B+p.b);
            if(!book[y][x])
            {
                a[k] = node(y, x, k, p.step, 6, p.pos+1);
                q.push(a[k]);
                book[y][x] = 1;
                k++;
            }
        }
    }
    if(!flag) printf("impossible\n");
    else //用栈存路径
    {
        printf("%d\n", p.pos);
        stack<node> s;
        while(1)
        {
            s.push(p);
            if(p.pre == -1) break;
            p = a[p.pre];
        }
        while(!s.empty())
        {
            int t = s.top().cmd;
            s.pop();
            if(t == 1)
                printf("FILL(1)\n");
            if(t == 2)
                printf("FILL(2)\n");
            if(t == 3)
                printf("DROP(1)\n");
            if(t == 4)
                printf("DROP(2)\n");
            if(t == 5)
                printf("POUR(2,1)\n");
            if(t == 6)
                printf("POUR(1,2)\n");

        }
    }
}
int main()
{
    while(~scanf("%d%d%d", &A, &B, &C))
    {
        memset(book, 0, sizeof(book));
        bfs();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值