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...
摘要由CSDN通过智能技术生成

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

题目如下

给你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(</
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值