(图的DFS遍历)分酒问题

有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

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
    	Scanner input = new Scanner(System.in);
    	
    	
    	String last = input.nextLine();
    	
    	Set<String> init = new HashSet<String>();
    	init.add("9 0 0 0");
    	Set<String> hist = new HashSet<String>();
    	hist.addAll(init);
    	int r = bfs(hist,init,last);
    	System.out.println(r);
    }
    
    //hist 记录历史状态
    //init 现在这一层所有状态
    public static int bfs(Set<String> hist,Set<String> init,String goStr){
    	//这一层有我想要的结果
    	if(hist.contains(goStr))return 0;
    	
    	//下一层的倒酒姿势
    	Set<String> form = new HashSet<String>();
    	
    	for (Iterator<String> iterator = init.iterator(); iterator.hasNext();) {
			String next = iterator.next();
			 Set<String> newForm = move(next);
			form.addAll(newForm);
		}
    	
    	//消除重复倒酒姿势
    	form.removeAll(hist);
    	
    	//如果消除重复倒酒姿势后大小 为 0 说明没有可以用的姿势了 -1 代表失败的意思,找不到结果
    	if(form.isEmpty())return -1;
    	
    	//载入历史倒酒姿势
    	hist.addAll(form);
    	
    	//递归调用 bfs 扩散开树的下一层开始宽度优先遍历 
    	int r = bfs(hist,form,goStr);
    	
    	
    	//出现失败情况
    	if(r<0)return -1;
    	
    	//否则 倒酒次数加1 返回回去
    	
    	return 1+r;
    }
    
    
    public static Set<String> move(String init){
    	int[] cap = {9,7,4,2};
    	int[] data = new int[4];
    	String[] ss = init.split(" ");
    	
    	Set<String> set = new HashSet<String>();
    	for(int i=0;i<ss.length;i++){
    		data[i] = Integer.parseInt(ss[i]);
    	}
    	
    	for(int i=0;i<4;i++){
    		for(int j=0;j<4;j++){
    			if(i==j)continue;
    			//A酒杯没酒了
    			if(data[i]==0)continue;
    			//B酒杯满了
    			if(data[j]==cap[j])continue;
    			
    			//A 全倒进 B 的总酒量
    			int sumWine = data[i] + data[j];
    			
    			//待会即将被改变的状态
    			int[] temp = Arrays.copyOf(data,data.length);
    			
    			//B酒杯装完全部A
    			if(sumWine<=cap[j]){
    				
    				temp[i] =0;
    				temp[j] = sumWine;
     				
    			}else{
    				//B酒杯不可以装满全部A
    				temp[i] = sumWine - cap[j];
    				temp[j] = cap[j];
    			}
    			
    			String res = "";
    			for(int k=0;k<4;k++){
    				res += " "+temp[k];
    			}
    			set.add(res.trim());
    		}
    		
    		
    	}
    	return set;
    	
    	
    
    }
    
    
    
    
   
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值