POJ 3414 Pots【bfs模擬倒水問題】

Pots
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 8253   Accepted: 3499   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


算法:數組模擬bfs


題意:


給你兩個容器 A  B 問是否能夠經過有限的步驟倒水,得到容量爲 C 的水
輸出最小的步數,同時輸出每一步的操作。
如果不能達到目標狀態,則輸出 impossible

思路:


總狀態只有那麼多, 反正每一步後都只有 6 種操作明顯的 BFS 關鍵是每一步路徑的記錄。
開始用 BFS + 容器做了一遍,發現還是無法處理好輸出路徑問題,只好重新開始用數組模擬。
容器雖然很好用又方便,但是在不考慮內存的狀況下,效率終究比不上數組模擬的了。

注意到 A 和 B 的範圍是 1 到 100 的整數,
那麼我們可以用vis[i][j]來記錄每一種狀態 0 <= i, j <= 100 ;
 i 表示目前 A 容器中的水, j 表示目前 B 容器中的水

應該很容易就能分析出,對應於每一種狀態的下一步只有六種情況:

一:用水池中的水裝滿 A
二:用水池中的水裝滿 B
三:把 A 中的水全部倒進廢水池
四:把 B 中的水全部倒進廢水池
五:把 A 中的水倒進 B 【不能溢出】
    那麼對應可能會有兩種狀態:用 k1 表示 A 中的水, k2 表示 B 中的水
    如果 k1+k2 <= B 則   k2 = k1+k2; k1 = 0 【不能夠裝滿容器 B】注意順序
    否則 k1 = k1+k2-B; k2 = B 【能夠裝滿容器 B】
六:把 B 中的水倒進 A 【不能溢出】
    也有兩種情況,分析同上
    如果 k1+k2 <= A 則 k1 = k1+k2; k2 = 0;
    否則 k2 = k1+k2-A; k1 = A

用結構體數組來模擬隊列
用 k1,k2 來記錄當前容器中水的狀態
前面已經分析過對應於每種情況只有 6 種操作, 那麼對應每種情況的操作記錄爲 1 到 6 輸出時處理下就好了。
當然少不了記錄到當前狀態最少用了多少步數 step
因爲要記錄路徑,所以定義一個 pre 來記錄上一步在數組模擬隊列中的下標。

最後如果能夠達到目的,在判定最後一步的時候記錄下最後一步在數組中的編號 lastIndex
然後從lastIndex從後往前找【pre】前一個步驟在數組中的編號存在 id[] 中
最後再按照掃描出的路徑依次遍歷即可。

code:


3414 Accepted 228K 0MS C++ 4165B
在此聲明一下代碼是我轉的,我感覺比較好,所以推薦給大家,希望可以幫助到像我一樣在bfs上有困難的同學
#include<stdio.h>
#include<string.h>

const int maxn = 110;
int vis[maxn][maxn]; //標記狀態是否入隊過
int a,b,c; //容器大小
int step; //最終的步數
int flag; //紀錄是否能夠成功

/* 狀態紀錄 */
struct Status{
    int k1,k2; //當前水的狀態
    int op; //當前操作
    int step; //紀錄步數
    int pre; //紀錄前一步的下標
}q[maxn*maxn];
int id[maxn*maxn]; //紀錄最終操作在隊列中的編號
int lastIndex; //最後一個的編號

void bfs()
{
    Status now, next;

    int head, tail;
    head = tail = 0;

    q[tail].k1 = 0; q[tail].k2 = 0;
    q[tail].op = 0; q[tail].step = 0; q[tail].pre = 0;

    tail++;

    memset(vis,0,sizeof(vis));
    vis[0][0] = 1; //標記初始狀態已入隊

    while(head < tail) //當隊列非空
    {
        now = q[head]; //取出隊首
        head++; //彈出隊首

        if(now.k1 == c || now.k2 == c) //應該不會存在這樣的情況, c=0
        {
            flag = 1;
            step = now.step;
            lastIndex = head-1; //紀錄最後一步的編號
        }

        for(int i = 1; i <= 6; i++) //分別遍歷 6 種情況
        {
            if(i == 1) //fill(1)
            {
                next.k1 = a;
                next.k2 = now.k2;
            }
            else if(i == 2) //fill(2)
            {
                next.k1 = now.k1;
                next.k2 = b;
            }
            else if(i == 3) //drop(1)
            {
                next.k1 = 0;
                next.k2 = now.k2;
            }
            else if(i == 4) // drop(2);
            {
                next.k1 = now.k1;
                next.k2 = 0;
            }
            else if(i == 5) //pour(1,2)
            {
                if(now.k1+now.k2 <= b) //如果不能夠裝滿 b
                {
                    next.k1 = 0;
                    next.k2 = now.k1+now.k2;
                }
                else //如果能夠裝滿 b
                {
                    next.k1 = now.k1+now.k2-b;
                    next.k2 = b;
                }
            }
            else if(i == 6) // pour(2,1)
            {
                if(now.k1+now.k2 <= a) //如果不能夠裝滿 a
                {
                    next.k1 = now.k1+now.k2;
                    next.k2 = 0;
                }
                else //如果能夠裝滿 b
                {
                    next.k1 = a;
                    next.k2 = now.k1+now.k2-a;
                }
            }

            next.op = i; //紀錄操作
            if(!vis[next.k1][next.k2]) //如果當前狀態沒有入隊過
            {
                vis[next.k1][next.k2] = 1; //標記當前狀態入隊
                next.step = now.step+1; //步數 +1
                next.pre = head-1; //紀錄前一步的編號

                //q.push(next);
                //q[tail] = next; 加入隊尾
                q[tail].k1 = next.k1; q[tail].k2 = next.k2;
                q[tail].op = next.op; q[tail].step = next.step; q[tail].pre = next.pre;
                tail++; //隊尾延長

                if(next.k1 == c || next.k2 == c) //如果達到目標狀態
                {
                    flag = 1; //標記成功
                    step = next.step; //紀錄總步驟數
                    lastIndex = tail-1; //紀錄最後一步在模擬數組中的編號
                    return;
                }
            }
        }
    }


}

int main()
{
    while(scanf("%d%d%d", &a,&b,&c) != EOF)
    {
        flag = 0; //初始化不能成功
        step = 0;

        bfs();
        if(flag)
        {
            printf("%d\n", step);

            id[step] = lastIndex; //最後一步在模擬數組中的編號
            for(int i = step-1; i >= 1; i--)
            {
                id[i] = q[id[i+1]].pre; //向前找前一步驟在模擬數組中的編號
            }

            for(int i = 1; i <= step; i++)
            {
                if(q[id[i]].op == 1)
                    printf("FILL(1)\n");

                else if(q[id[i]].op == 2)
                    printf("FILL(2)\n");

                else if(q[id[i]].op == 3)
                    printf("DROP(1)\n");

                else if(q[id[i]].op == 4)
                    printf("DROP(2)\n");

                else if(q[id[i]].op == 5)
                    printf("POUR(1,2)\n");

                else if(q[id[i]].op == 6)
                    printf("POUR(2,1)\n");
            }
        }
        else printf("impossible\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章