现在的位置: 首页 > 综合 > 正文

Pots(POJ 3414)解题报告(虫前,有两个痛)

2018年12月15日 ⁄ 综合 ⁄ 共 6743字 ⁄ 字号 评论关闭

Pots(POJ 3414)解题报告(虫前,有两个痛)

//代码参考自http://blog.csdn.net/sunacmer/article/details/3945312

一、原题

E - Pots

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

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

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

 

二、题目大意:

虫前,有两个痛,A痛和B痛,还有一痛水,容量我们叫它C。。。

你要想办法在两个痛之间倒腾来倒腾去最后整出来C。。。

规则如下:

1、你可以把任何一个痛装满水。

2、你可以把任何一个痛里的水倒干净。

3、你可以把一个痛里的水倒进另一个痛里。如果有剩余,留在那个痛里。。。

事实就是,一共有六种情况。

1:FILL(1)

2:FILL(2)

3:DROP(1)

4:DROP(2)

5:POUR(1,2)

6:POUR(2,1)

然后现在依次输入ABC,要求计算出最少步数以及打印出每一步。

 

三、基本思路:BFS

1、用一个结构体存相关数据(当前状态)

typedef struct

{

    int a;

    int b;

    int step;

    int pre;

    int flag;//标记当前步数step,之前的一步preflag表示六种情况中的一种

}Pot;

这里为了可以实现打印数据,必须在结构里面加上preflag

Queue在经历BFS后结束在front+1处,故而为了得到之前被遍历的结果,必须加一个pre用于最后的查找。

 

2、主要用到的几个关键变量

Pot que[N];//模拟队列

bool visit[101][101];//记录当前的A状态和B状态的过程是否被访问过

int path[N],index;//记录全部路径在que中的下标

这里第一个使用了数组来模拟队列而不是直接用queue<int>que,主要在于无法打印路径,由于que并不记录前一个节点位置,所以只好用数组进行模拟,便于通过下标进行回查。

BFS结束后,用path数组记录que下标。

 

3BFS关键代码部分:

这部分的嵌套循环实质是对当前的一个状态(存储在temp里)向下查找下一个状态。

过程依旧是:

头元素入队列,标记头元素已被查找;

While循环判断队列非空,取头元素(标记),弹出头元素,判断结束条件(找到C),遍历查找下一状态。

    while(front!=rear)

    {

        temp=que[front];//取头元素

        front++;//弹出头元素

        if(temp.a==C||temp.b==C)

        {

            flag_finish=true;

            break;

        }

        for(int i=1;i<=6;i++)//6种情况

        {

            switch(i)

            {

                case 1:next.a=A;next.b=temp.b;next.flag=1;break;//fill(1)

……

                case 5://drop(1,2)

                {

                    if(B-temp.b<temp.a)//如果A倒完后有剩余

                    {

                        next.a=temp.a-(B-temp.b);

                        next.b=B;

                        next.flag=5;

                    }

                    else//如果A能全倒进B

                    {

                        next.a=0;

                        next.b=temp.b+temp.a;

                        next.flag=5;

                    }

                }break;

……

            }

            if(!visit[next.a][next.b])

            {

               next.step=temp.step+1;

               next.pre=front-1;//front++

               que[rear++]=next;//next压入队列

               visit[next.a][next.b]=true;//该状态被访问过

//             cout<<front<<":"<<next.flag<<endl;

            }

        }

    }

4、记录被遍历过的元素下标并打印结果    

index=que[front-1].step-1;//que[front-1].step步,但后面到0,所以-1

    for(int i=front-1;i>=0;)//将之前已经弹出队列的元素从最后一位到第一位的下标存进path

    {

        path[index--]=i;

        i=que[i].pre;

    }

    for(int i=0;i<que[front-1].step;i++)

    {

        switch(que[path[i]].flag)

        {

            case 1:cout<<"FILL(1)"<<endl;break;

    ……

        }

}

 

四、源码完整版

#include <iostream>

#include <cstring>

#define N 10010

using namespace std;

typedef struct

{

    int a;

    int b;

    int step;

    int pre;

    int flag;//标记当前步数step,之前的一步preflag表示六种情况中的一种

}Pot;

Pot que[N];//模拟队列

bool visit[101][101];//记录当前的A状态和B状态的过程是否被访问过

int path[N],index;//记录全部路径在que中的下标

int main()

{

    int A,B,C;

    bool flag_finish=false;//初始化记录是否找到最终的结果

    cin>>A>>B>>C;

    memset(visit,0,sizeof(visit));

    memset(que,0,sizeof(que));

    int front=0,rear=0;//队列顶,尾

    Pot temp={0,0,0,-1,0},next;//初始化队列头

    que[rear++]=temp;//首元素压入队列

    visit[0][0]=true;

    while(front!=rear)

    {

        temp=que[front];//取头元素

        front++;//弹出头元素

        if(temp.a==C||temp.b==C)

        {

            flag_finish=true;

            break;

        }

        for(int i=1;i<=6;i++)//6种情况

        {

            switch(i)

            {

                case 1:next.a=A;next.b=temp.b;next.flag=1;break;//fill(1)

                case 2:next.a=temp.a;next.b=B;next.flag=2;break;//fill(2)

                case 3:next.a=0;next.b=temp.b;next.flag=3;break;//drop(1)

                case 4:next.a=temp.a;next.b=0;next.flag=4;break;//drop(2)

                case 5://drop(1,2)

                {

                    if(B-temp.b<temp.a)//如果A倒完后有剩余

                    {

                        next.a=temp.a-(B-temp.b);

                        next.b=B;

                        next.flag=5;

                    }

                    else//如果A能全倒进B

                    {

                        next.a=0;

                        next.b=temp.b+temp.a;

                        next.flag=5;

                    }

                }break;

                case 6://drop(2,1)

                {

                    if(A-temp.a<temp.b)//如果B倒完后有剩余

                    {

                        next.b=temp.b-(A-temp.a);

                        next.a=A;

                        next.flag=6;

                    }

                    else//如果B能全倒进A

                    {

                        next.b=0;

                        next.a=temp.b+temp.a;

                        next.flag=6;

                    }

                }break;

            }

            if(!visit[next.a][next.b])

            {

               next.step=temp.step+1;

               next.pre=front-1;//front++

               que[rear++]=next;//next压入队列

               visit[next.a][next.b]=true;//该状态被访问过

//             cout<<front<<":"<<next.flag<<endl;

            }

        }

    }

    if(!flag_finish)   {cout<<"impossible"<<endl;return 0;}//必须加上return否则继续执行后续部分

    else cout<<que[front-1].step<<endl;

    index=que[front-1].step-1;//que[front-1].step步,但后面到0,所以-1

    for(int i=front-1;i>=0;)//将之前已经弹出队列的元素从最后一位到第一位的下标存进path

    {

        path[index--]=i;

        i=que[i].pre;

    }

    for(int i=0;i<que[front-1].step;i++)

    {

        switch(que[path[i]].flag)

        {

            case 1:cout<<"FILL(1)"<<endl;break;

            case 2:cout<<"FILL(2)"<<endl;break;

            case 3:cout<<"DROP(1)"<<endl;break;

            case 4:cout<<"DROP(2)"<<endl;break;

            case 5:cout<<"POUR(1,2)"<<endl;break;

            case 6:cout<<"POUR(2,1)"<<endl;break;

        }

    }

    return 0;

}

 

 

PS:最后发现那个flag还是可以不用的。。。。囧,结束条件也可以是iffront==rearcout<<"impossible"<<endl;

另,附上链接:

http://poj.org/problem?id=3414

抱歉!评论已关闭.