分红酒(有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升 开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着)

分红酒

有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

  思路:使用广度优先搜索,在队列中每弹出一个元素,就讨论这种元素倒酒的所有情况,倒酒用一个二重for循环来模拟,倒洒有两种情况:把一个瓶子倒满或把一个瓶子倒空。用一个Set集合来存放各种情况,对于每弹出一个元素衍生出的每种情况,都在Set集合里检查是否曾经出现过,如果没出现过就加入Set集合。
代码实现:
import java.util.*;

public class Main {
    static class Node {
        String val;//4个酒瓶的状态
        int depth;//操作次数

        public Node(String val, int depth) {
            this.val = val;
            this.depth = depth;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = "9000";//初始状态
        Node node = new Node(s, 0);
        Queue<Node> queue = new LinkedList();
        queue.add(node);
        String input = sc.nextLine().replace(" ", "");
        char[] full = {'9', '7', '4', '2'};//最大容量
        Set<String> set = new HashSet<>();
        set.add(s);
        int bfs = bfs(queue, input, full, set);
        System.out.println(bfs);
    }

    private static int bfs(Queue<Node> queue, String input, char[] full, Set<String> set) {
        int depth = 0;
        while (!queue.isEmpty()) {
            ++depth;
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                Node poll = queue.poll();
                if (input.equals(poll.val)) {//如果找到目标就返回深度
                    return poll.depth;
                }
                char[] curr = poll.val.toCharArray();
                for (int j = 0; j < 4; ++j) {//这里的双重for循环模拟倒酒操作
                    for (int k = 0; k < 4; ++k) {
                        if (j == k || curr[j] <= 0 || curr[k] == full[k]) {
                            continue;
                        }
                        char[] temp = new char[4];
                        for (int l = 0; l < 4; ++l) {
                            temp[l] = curr[l];
                        }
                        //把另一个酒瓶子倒满
                        if (curr[j] >= full[k] - curr[k] + '0') {//这里是一个坑,千万别把后面的'0'忘了加上
                            temp[j] = (char) (curr[j] - (full[k] - curr[k]));
                            temp[k] = full[k];
                            String newStr = new String(temp);
                            if (!set.contains(newStr)) {
                                set.add(newStr);
                                queue.add(new Node(newStr, depth));
                            }//以下操作是把一个酒瓶子倒空
                        } else if (curr[j] < full[k] - curr[k] + '0') {//这里是一个坑,千万别把后面的'0'忘了加上
                            temp[j] = '0';
                            temp[k] = (char) (curr[j] + curr[k] - '0');//这里是一个坑,千万别把后面的'0'忘了减去
                            String newStr = new String(temp);
                            if (!set.contains(newStr)) {
                                set.add(newStr);
                                queue.add(new Node(newStr, depth));
                            }
                        }
                    }
                }
            }
        }
        return -1;//所有元素都出队了,都没有找到目标,直接返回-1
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值