有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
public class Wine {
static Set<String> move(String s){
Set<String> set = new HashSet<>();
String[] ss = s.split(" ");
int[] data = new int[4];
int[] cap = {9,7,4,2};
for(int i=0; i<ss.length; i++) {
data[i] = Integer.parseInt(ss[i]);
}
//从i倒入j
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
if(i==j) continue;
if(data[j]==cap[j]) continue;//源满
if(data[i]==0) continue;//目标满
int sum = data[i] + data[j];
int vi = (sum <= cap[j]) ? 0 : sum - cap[j];//倒酒后i的新值
int vj = (sum <= cap[j]) ? sum : cap[j];//倒酒后j的新值
//生成新的状态串
String news = "";
for(int k=0; k<4; k++) {
if(k==i) news += vi + " ";
else if(k==j) news += vj + " ";
else news += data[k] + " ";
}
set.add(news.trim());
}
}
return set;
}
static int f(Set<String> hist, Set<String> from, String goal) {
if(from.contains(goal)) return 0;
Set<String> from2 = new HashSet<>();
for(String s:from) {
Set<String> t = move(s);
from2.addAll(t);
}
from.addAll(from2);
from.removeAll(hist);
if(from.isEmpty()) return -1;
hist.addAll(from2);
int r = f(hist, from, goal);
if(r<0) return -1;
return r + 1;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Set<String> from = new HashSet<>();
from.add("9 0 0 0");
Set<String> hist = new HashSet<>();
hist.addAll(from);
System.out.println(f(hist,from,sc.nextLine().trim()));
sc.close();
}
}