一、题目描述
Pots
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 29772 Accepted: 12428 Special Judge
Description
You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:
FILL(i) fill the pot i (1 ≤ i ≤ 2) from the tap;
DROP(i) empty the pot i to the drain;
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 A, B, 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
二、算法分析说明与代码编写指导
一共有六种操作:
“FILL(1)”,“DROP(1)”,“POUR(1,2)”,“FILL(2)”,“DROP(2)”,“POUR(2,1)”
可以采用 BFS 得到次数最少的操作序列之一。这种搜索可以看成在 6 叉树上从根节点开始进行 BFS 求最短路。树的根节点不含任何有效的信息,设其它节点都存储如下信息:
操作类型 op(0 ~ 5)、进行该类型操作后两个罐的水量 a,b、上一步操作的存储位置 last。
树可以按照层次遍历的顺序进行存储,而且可以一边进行 BFS 一边构建。
题目要求给出具体的操作序列,所以我们不用 queue,而是改用 vector 存储节点,因为进行路径还原更方便。
bool 型变量 f 代表是否找到了最短路径。
六种操作并不是在任何时候都可以进行的。在搜索下一步之前要先判定操作是否可以正确进行。
FILL(1),只有在 1 罐不装满水的时候可以进行。
DROP(1),只有在 1 罐非空的时候可以进行。
POUR(1, 2),只有在 1 罐非空且 2 罐没有装满时可以进行。
FILL(2),只有在 2 罐不装满水的时候可以进行。
DROP(2),只有在 2 罐非空的时候可以进行。
POUR(2, 1),只有在 2 罐非空且 1 罐没有装满时可以进行。
节点 n 用于向 vector 插入新节点。在向节点 n 写入信息时要注意:虽然有的操作只对 1 个罐进行(FILL / POUR),但必须向 n 一同写入当前节点的另一个罐的信息,否则另一个罐的信息是来自上一个新插入的节点而不是当前正在搜寻的节点,从而导致给出错误的结果。
bitset v 用于刻画一个状态(在进行本次操作后)是否已经在序列中。如果已经存在,则不要添加,因为这可能会导致最终找到的序列中,有若干段的操作是完全重复的,此时给出的步数自然不是最少的。但并不是在一出现重复的状态就停止搜索并报告 impossible,因为还有其它的操作可以尝试(一开始就是没想到这一点搞到我在样例卡了特别久)。
当遍历完已有的操作序列后,若仍未找到符合要求的状态(1、2 两个罐有一个的储水量为 c),则意味着不可能找到相应的方案,因为在最后搜寻到的节点之后尝试任何操作都会导致序列出现重复,也就是说顺着搜索树的任何一条从根到叶的路径走,都会令两个罐的水量随着时间推移不断循环但就是不为 c。
设栈 r 用于储存结果。当出现符合要求的状态后,BFS 就可以终止,然后从序列的末端开始不断根据节点的 last 值从后往前将相应的操作添加到栈中,然后从栈顶开始输出即可。节点 0 不含任何有效信息,因此向前跳到 0 节点时应该停止往栈中压入操作。
三、AC 代码(0 ms)
注:本代码采用 C++ 提交时 AC,G++ 提交时 WA,原因不明。
#include<cstdio>
#include<vector>
#include<stack>
#include<bitset>
#pragma warning(disable:4996)
using namespace std;
//struct node { unsigned op = 0, a = 0, b = 0; size_t last = 0; };
struct node {
unsigned op, a, b; size_t last; };
const char* const op[] = {
"FILL(1)","DROP(1)","POUR(1,2)","FILL(2)","DROP(2)","POUR(2,1)" };
unsigned a, b, c; vector<node> s; node n; stack<unsigned> r; bitset<101> v[101]; bool f =