分红酒
有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升
开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着。
允许把酒从一个瓶子倒入另一个瓶子,但只能把一个瓶子倒满或把一个瓶子倒空,不能有中间状态。这样的一次倒酒动作称为1次操作。
假设瓶子的容量和初始状态不变,对于给定的目标状态,至少需要多少次操作才能实现?
本题就是要求你编程实现最小操作次数的计算。
输入:最终状态(逗号分隔)
输出:最小操作次数(如无法实现,则输出-1)
例如:
输入:
9,0,0,0
应该输出:
0
输入:
6,0,0,3
应该输出:
-1
输入:
7,2,0,0
应该输出:
2
思路:使用广度优先搜索,在队列中每弹出一个元素,就讨论这种元素倒酒的所有情况,倒酒用一个二重for循环来模拟,倒洒有两种情况:把一个瓶子倒满或把一个瓶子倒空
。用一个Set集合来存放各种情况,对于每弹出一个元素衍生出的每种情况,都在Set集合里检查是否曾经出现过,如果没出现过就加入Set集合。
代码实现:
import java.util.*;
public class Main {
static class Node {
String val;//4个酒瓶的状态
int depth;//操作次数
public Node(String val, int depth) {
this.val = val;
this.depth = depth;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = "9000";//初始状态
Node node = new Node(s, 0);
Queue<Node> queue = new LinkedList();
queue.add(node);
String input = sc.nextLine().replace(" ", "");
char[] full = {'9', '7', '4', '2'};//最大容量
Set<String> set = new HashSet<>();
set.add(s);
int bfs = bfs(queue, input, full, set);
System.out.println(bfs);
}
private static int bfs(Queue<Node> queue, String input, char[] full, Set<String> set) {
int depth = 0;
while (!queue.isEmpty()) {
++depth;
int size = queue.size();
for (int i = 0; i < size; ++i) {
Node poll = queue.poll();
if (input.equals(poll.val)) {//如果找到目标就返回深度
return poll.depth;
}
char[] curr = poll.val.toCharArray();
for (int j = 0; j < 4; ++j) {//这里的双重for循环模拟倒酒操作
for (int k = 0; k < 4; ++k) {
if (j == k || curr[j] <= 0 || curr[k] == full[k]) {
continue;
}
char[] temp = new char[4];
for (int l = 0; l < 4; ++l) {
temp[l] = curr[l];
}
//把另一个酒瓶子倒满
if (curr[j] >= full[k] - curr[k] + '0') {//这里是一个坑,千万别把后面的'0'忘了加上
temp[j] = (char) (curr[j] - (full[k] - curr[k]));
temp[k] = full[k];
String newStr = new String(temp);
if (!set.contains(newStr)) {
set.add(newStr);
queue.add(new Node(newStr, depth));
}//以下操作是把一个酒瓶子倒空
} else if (curr[j] < full[k] - curr[k] + '0') {//这里是一个坑,千万别把后面的'0'忘了加上
temp[j] = '0';
temp[k] = (char) (curr[j] + curr[k] - '0');//这里是一个坑,千万别把后面的'0'忘了减去
String newStr = new String(temp);
if (!set.contains(newStr)) {
set.add(newStr);
queue.add(new Node(newStr, depth));
}
}
}
}
}
}
return -1;//所有元素都出队了,都没有找到目标,直接返回-1
}
}