java实现,关于无刻度水桶倒水的问题

字节跳动一道笔试题让我纠结了许久

题目如下

给你3个无刻度的水桶,问能不能弄出k升的水,如果能,最小需要多少步,不能打印no,
输入:4个数,a代表水桶一容量,b水桶二,c水桶3,k要称出的容量
输出:no或者最少次数
例如:8,3,5,4 输出6
[0,0,5] -> [0,3,2] -> [0,0,2] -> [0,2,0] -> [0,2,5] -> [0,3,4]
例如: 8,4,6,5 输出no
因为偶数不能称出奇数

基础知识

以前做过这样类似的题,如何判断能称出来呢,存在这样的x,y,z整数使得
k = x * a+y * b+z * c;
之前想遍历出a,b,c从一个范围到另一个范围,得出x,y,z 根据x,y,z,来判断多少次,可是我根本就判断不出来

第二次解决方法

广义优先和动态规划
* 每个桶都有两个选择,倒水和加水,利用广义优先,记录出第一步所有的3个桶状态
* 第二步在第一步基础上接着这样做,直到出现k升水
* 为了防止出现重复的,我们必须存下每次操作所有桶出现的状态,每一次操作判断该状态是否存在
* 存在就不做了,所以,查询操作是极度多的,优化查找是优化这个算法的核心
* 我们利用HashMap存储,k是状态,v是次数,为了让出现的桶状态如4,7,8 和后续出现的为一个我们
* 将状态用一个3位数表示,即a100+b10+c表示。
* 每次出现一个这样的数,判断包含否,不包含加入hashtable

代码有详细的解释

这个需要极大细心,每步都很相似又有些不同,思路可以,调试了半天

/**
 * @time201.09.10
 * @author 明行
 *题目:给3个无刻度水桶,a,b,c,能否称出k升水,如果能输入最小多少次,不能输出no
 * 输入,4个数,分别是a,b,c,k 输出no或者最小次数
 */

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Scanner;
public class Get_Water {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int a = sc.nextInt();
		int b = sc.nextInt();
		int c = sc.nextInt();
		int k = sc.nextInt();
		// 先作预判断看能不能称出来
		if(k > a + b + c) System.out.println("no");
		else {
			//如果3个桶都是偶数,称奇数水,则不能
			if(((a&1)==0) && ((b&1)==0) && ((c&1)==0) && ((k&1)==1)) {
				System.out.println("no");
			}
			else {
				getResult(a,b,c,k);
			}
		}
	}
	/*解题思路
	 * 广义优先和动态规划
	 * 	每个桶都有两个选择,倒水和加水,利用广义优先,记录出第一步所有的3个桶状态
	 * 第二步在第一步基础上接着这样做,直到出现k升水
	 * 为了防止出现重复的,我们必须存下每次操作所有桶出现的状态,每一次操作判断该状态是否存在
	 * 存在就不做了,所以,查询操作是极度多的,优化查找是优化这个算法的核心
	 * 我们利用HashMap存储,k是状态,v是次数,为了让出现的桶状态如4,7,8 和后续出现的为一个我们
	 * 将状态用一个3位数表示,即a*100+b*10+c表示。
	 * 每次出现一个这样的数,判断包含否,不包含加入hashtable
	 */
	private static void getResult(int a, int b, int c, int k) {
		/* 选择HashMap原因,其实后面发现选择HashSet更好  
		HashTable较适应于多线程 但被淘汰了改用ConCurrentHashMap
		 * 对于集合里的值增删查和get的时间复杂度分析:顺序增删查改,_ 代表没有该方法获无意义
		 * Array    数组    O(n) O(n) O(n) O(1)
     Listed list    链表    O(n) O(n) O(n) O(1)
         HashMap   哈希表   O(1) O(1) O(1)  _
       HashTable   哈希表   O(1) O(1) O(1)  _
        TreeMap 红黑树有序  O(logn) O(logn) O(logn)
        TreeSet 红黑树有序  O(logn) O(logn) O(logn)
        HashSet    哈希表  O(1) O(1) O(1)  _
 LinkedHashSe 链表实现的哈希表  O(1) O(1) O(1)  _
		 */
		
		/* 动态规划加广义优先,所以应该用队列来做。*/
		HashMap<Integer,Integer> map = new HashMap<>();
		LinkedList<int[]> q = new LinkedList<>();
		int[] csh = new int[3]; //用一个长度为3的数组记录每个桶的状态
		map.put(0, 0); // 放入初始状态
		q.offer(csh);
		int res = -1;
		/* 每一层操作完,步数加一 */
		int i = 1; //记录第一层的个数
		int step = 0; //记录步数 
		// 只要q1为空,所有情况都遍历完了
		while(!q.isEmpty()) {
			// 每次弹出,需要考虑引用类型的问题,所以,做副本
			// 难点二,做几个副本,
			int[] temp = q.poll(); // temp是开始的基础,每次在他上面做副本,进行下一步操作
			// 先判断弹出的节点是否满足情况,满足就可以返回了
			if(heli(temp,k)) {
				res = step;break;
			}
			int[] fuben = temp.clone();
			// 按顺序对a,b,c3个桶做倒水或加水操作
			// 如果为0,只能加水,为a只能倒水
			//其他,0-a之间,加水没有意义,所以也是只能到水
			//倒水有三个选择,到掉和导入其他两个桶里
			
			/**注:step指的是弹出的节点所用的步数,如果在次基础上多做一步要加一 **/
			
			/**桶一操作**/
			if(fuben[0] == 0) {
				// 判断map是否存在该状态
				int zt = a * 100 + fuben[1] * 10 + fuben[2];
				if(!map.containsKey(zt)) {
					// 记录状态,加入队列
					map.put(zt, step+1);
					fuben[0] = a;
					q.offer(fuben);
					// 重置副本
					fuben = temp.clone();
				}
			}else {
				//倒水有三个选择,到掉和到入其他两个桶里
				/*倒掉*/
				int zt = fuben[1] * 10 + fuben[2]; 
				if(!map.containsKey(zt)) {
					// 记录状态,加入队列
					map.put(zt, step+1);
					fuben[0] = 0;
					q.offer(fuben);
					// 重置副本
					fuben = temp.clone();
				}
				/* 导入第二个桶 */
				// 第二个桶没满
				if(fuben[1] != b) {
					//导入第二个桶里(第二个桶不能放下)
					if(fuben[0] > b - fuben[1] ) {
						zt = (fuben[0] - b + fuben[1]) *100 + b*10 + fuben[2];
						if(!map.containsKey(zt)) {
							// 记录状态,加入队列
							map.put(zt, step+1);
							fuben[0] = fuben[0] - b + fuben[1];
							fuben[1] = b;
							q.offer(fuben);
							// 重置副本
							fuben = temp.clone();
						}
					}
					// 第二个桶可以放下
					else {
						zt = (fuben[1] + fuben[0])*10+fuben[2];
						if(!map.containsKey(zt)) {
							// 记录状态,加入队列
							map.put(zt, step+1);
							fuben[0] = 0;
							fuben[1] = fuben[1] + fuben[0];
							q.offer(fuben);
							// 重置副本
							fuben = temp.clone();
						}
					}
				}
				/* 导入第三个桶 */
				// 第三个桶没满
				if(fuben[2] != c) {
					//导入第三个桶里(第三个桶不能放下)
					if(fuben[0] > c - fuben[2]) {
						zt = (fuben[0] + fuben[2] - c)*100+fuben[1]*10+c;
						if(!map.containsKey(zt)) {
							// 记录状态,加入队列
							map.put(zt, step+1);
							fuben[0] = fuben[0] - c + fuben[2];
							fuben[2] = c;
							q.offer(fuben);
							// 重置副本
							fuben = temp.clone();
						}
					}
					//第三个桶能放下
					else {
						zt = fuben[1]*10 + (fuben[0] + fuben[2]);
						if(!map.containsKey(zt)) {
							// 记录状态,加入队列
							map.put(zt, step+1);
							fuben[0] = 0;
							fuben[2] = fuben[0] + fuben[2];
							q.offer(fuben);
							// 重置副本
							fuben = temp.clone();
						}
					}
				}
			}
			
			/** 后面都是同理操作 **/
			
			/** 桶二操作 **/
			if(fuben[1] == 0) {
				int zt = fuben[0]*100+b*10+fuben[2];
				if(!map.containsKey(zt)) {
					map.put(zt, step+1);
					fuben[1] = b;
					q.offer(fuben);
					fuben = temp.clone();
				}
			}else {
				// 倒水
				int zt = fuben[0]*100+fuben[2];
				if(!map.containsKey(zt)) {
					map.put(zt, step+1);
					fuben[1] = 0;
					q.offer(fuben);
					fuben = temp.clone();
				}
				// 到桶1
				if(fuben[0] != a) {
					if(fuben[1] > a - fuben[0]) {
						zt = a*100+(fuben[1]+fuben[0]-a)*10+fuben[2];
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[0] = a;
							fuben[1] = fuben[1]+fuben[0]-a;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}else {
						zt = (fuben[1]+fuben[0])*100+fuben[2];
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[0] = fuben[1]+fuben[0];
							fuben[1] = 0;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}
				}
				//到桶3
				if(fuben[2] != c) {
					if(fuben[1] > c - fuben[2]) {
						zt = fuben[0]*100+(fuben[1]+fuben[2]-c)*10+c;
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[2] = c;
							fuben[1] = fuben[1]+fuben[2]-c;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}else {
						zt = fuben[0]*100+fuben[1]+fuben[2];
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[2] = fuben[1]+fuben[2];
							fuben[1] = 0;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}
				}
			}
			/** 桶三操作 **/
			if(fuben[2] == 0) {
				int zt = fuben[0]*100+fuben[1]*10+c;
				if(!map.containsKey(zt)) {
					map.put(zt, step+1);
					fuben[2] = c;
					q.offer(fuben);
					fuben = temp.clone();
				}
			}else {
				// 倒水
				int zt = fuben[0]*100+fuben[1]*10;
				if(!map.containsKey(zt)) {
					map.put(zt, step+1);
					fuben[2] = 0;
					q.offer(fuben);
					fuben = temp.clone();
				}
				// 到桶1
				if(fuben[0] != a) {
					if(fuben[2] > a - fuben[0]) {
						zt = a*100+fuben[1]*10+(fuben[2]+fuben[1]-a);
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[0] = a;
							fuben[1] = fuben[2]+fuben[0]-a;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}else {
						zt = (fuben[2]+fuben[0])*100+fuben[1]*10;
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[0] = fuben[2]+fuben[0];
							fuben[2] = 0;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}
				}
				//到桶2
				if(fuben[1] != b) {
					if(fuben[2] > b - fuben[1]) {
						zt = fuben[0]*100+b*10+fuben[1]+fuben[2]-b;
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[2] = fuben[1]+fuben[2]-b;
							fuben[1] = b;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}else {
						zt = fuben[0]*100+(fuben[1]+fuben[2])*10;
						if(!map.containsKey(zt)) {
							map.put(zt, step+1);
							fuben[1] = fuben[1]+fuben[2];
							fuben[2] = 0;
							q.offer(fuben);
							fuben = temp.clone();
						}
					}
				}
			}
			// 判断这一层是否循环完毕
			i--;
			if(i == 0) {
				step++;
				i = q.size();
				/* 打印map 查看和调试 */
				// print(map, step);
				
				
			}
		}
		if(res != -1)
			System.out.println(res);
		else System.out.println("no");
	}
	
	
	// 判断是否满足情况
	private static boolean heli(int[] temp, int k) {
		if(temp[0] == k) return true;
		if(temp[1] == k) return true;
		if(temp[2] == k) return true;
		if((temp[0] + temp[1]) == k) return true;
		if((temp[0]) + temp[2] == k) return true;
		if((temp[1] + temp[2]) ==k) return true;
		if((temp[0]+temp[1]+temp[2])==k) return true;
		return false;
	}
	
	// 打印map集合来调试
	private static void print(HashMap<Integer, Integer> map , int step) {
		/* 查看测试数据 */ 
		Iterator<Integer> it = map.keySet().iterator();
		System.out.print(step+"==>");
		while(it.hasNext()) {
			System.out.print(it.next()+",");
		}
		System.out.println();
	}
}

你可能感兴趣的:(Java)