蓝桥杯:分酒问题

有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();
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44260464/article/details/105714486
今日推荐