zoukankan      html  css  js  c++  java
  • 分酒问题(DFS解法)

    题目大概是这样:

    已知有三个容量分别为3千克、5千克和8千克的并且是没有刻度的酒瓶,3千克和5千克的瓶子均装满了酒,而8千克的瓶子为空。现要求仅用这三个酒瓶将这些酒均分为两个4千克并分别装入5千克和8千克的瓶子中。

    题解:

    可以扩展为有n个瓶子,每个瓶子当前装了x1,x2,x3…xn的酒,每个瓶子的上限是y1,y2,…yn,目标状态是每个瓶子d1,d2,…dn,现在要从当前状态转换到目标状态

    可以解读到,每个瓶子只有两种状态--要么盛满,要么空

    所以当酒从x瓶子转移到y瓶的时候,只有可能是试图将酒全部到入y瓶中,这样会造成两种结果:

      能盛得下-- x瓶空,y瓶的酒为原来的酒加上x瓶原来的酒。

      盛不下-- x瓶的酒为原来的酒减去倒过去的那部分, y瓶满。

    很显然,如果要求最短的步数,BFS是一个比较简单的办法,现在想输出所有的路径,所以考虑DFS

    代码:

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Scanner;
    
    //已知有3个容量分别为3kg,5kg和8kg且没有刻度的酒瓶3kg和5kg的瓶子均装满了酒。而8kg的瓶子为空。
    //现要求仅用这3个酒瓶将这些酒均分为两个4kg,并分别装入5kg和8kg的瓶子中。
    
    public class DispensingProblem {
        public static int N;  //酒瓶的数量
        public static Integer[] bottleMax;  //每个酒瓶最多装多少酒
        public static Integer[] bottleCurrent;  //每个酒瓶现在装了多少酒
        public static Integer[] bottleFinal;   //标注每个酒瓶的最终状态
        public static ArrayList<String> states;   //标注每一种状态,防止重复(每一种状态是一个向量,我用整型数组表示)
        public static ArrayList<String[]> paths;   //标注每一条路径
        public static int caseNum = 0;   //标注是第几种方法
        public static int minNum = 1000000;  //标注最少需要的步数
        public static void dfs(int n){
            String testS = String.valueOf(bottleCurrent[0]);
            for(int m =1;m<N;m++)
                testS+=String.valueOf(bottleCurrent[m]);
            boolean flag = true;
            for(int k = 0;k<N;k++)
                if(bottleCurrent[k]!=bottleFinal[k])
                    flag = false;
            if(flag){
                caseNum ++ ;
                if(n <= minNum){
                    if(n<minNum){
                        paths.clear();    
                    }
                    String[] tempS= new String[states.size()];
                    for(int ll = 0;ll<states.size();ll++)
                        tempS[ll] = states.get(ll);
                    paths.add(tempS);
                    minNum = n;
                }
                System.out.println("第"+caseNum+"种方法:");
                for(int l=0;l<states.size();l++)
                    System.out.println(states.get(l));
                System.out.println("总共需要移动"+n+"步");
                System.out.println("------------------------------------");
                return;
            }
            //找出当前可能的所有移动
            //数据不大,不需要优化
            //每个瓶子只能倒满或者倒空
            //注意要标注每一种状态,防止状态重复
            for(int i = 0 ; i < N;i++)
                for(int j = 0; j < N ;j++){
                    if(i==j)
                        continue;
                    //从i瓶把所有酒倒入j瓶
                    int nI = bottleCurrent[i];
                    int nJ = bottleCurrent[j];
                    int temp = nI + nJ - bottleMax[j];
                    if(temp<=0){
                        //能全倒进去
                        bottleCurrent[i] = 0;
                        bottleCurrent[j] = nI + nJ;
                        String s = String.valueOf(bottleCurrent[0]);
                        for(int m =1;m<N;m++)
                            s+=String.valueOf(bottleCurrent[m]);
                        if(!states.contains(s)){
                            states.add(s);
                            dfs(n+1);
                            //回溯
                            states.remove(states.indexOf(s));
                        }
                        //回溯
                        bottleCurrent[i] = nI;
                        bottleCurrent[j] = nJ;
                    }
                    else{
                        //不能全倒进去
                        bottleCurrent[i] = temp;
                        bottleCurrent[j] = bottleMax[j];
                        String s = String.valueOf(bottleCurrent[0]);
                        for(int m =1;m<N;m++)
                            s+=String.valueOf(bottleCurrent[m]);
                        if(!states.contains(s)){
                            states.add(s);
                            dfs(n+1);
                            //回溯
                            states.remove(states.indexOf(s));
                        }
                        //回溯
                        bottleCurrent[i] = nI;
                        bottleCurrent[j] = nJ;
                    }
                }
            //找遍所有状态都不可行,则表明不能出现这种状态
            //System.out.println("不可能存在这种状态!");
        }
        public static void main(String[] args){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入瓶子个数");
            N = sc.nextInt();
            bottleMax = new Integer[N];
            bottleCurrent = new Integer[N];
            bottleFinal = new Integer[N];
            states = new ArrayList<String>();
            paths = new ArrayList<String[]>();
            System.out.println("请输入每个瓶子的容量");
            for(int i = 0 ;i < N; i ++){
                bottleMax[i] = sc.nextInt();
            }
            System.out.println("请输入每个瓶子当前有多少酒");
            for(int i = 0 ;i < N; i ++){
                bottleCurrent[i] = sc.nextInt();
            }
            System.out.println("请输入最终希望每个瓶子有多少酒");
            for(int i = 0 ;i < N; i ++){
                bottleFinal[i] = sc.nextInt();
            }
            String s = String.valueOf(bottleCurrent[0]);
            for(int i =1;i<N;i++)
                s+=String.valueOf(bottleCurrent[i]);
            states.add(s);
            dfs(0);
            System.out.println("******************************");
            System.out.println("最少需要"+minNum+"步");
            int index = 0;
            for(int i = 0;i<paths.size();i++){
                index++;
                System.out.println("第"+index+"种最短方法:");
                String[] temp = paths.get(i);
                for(int j = 0;j<temp.length;j++)
                    System.out.println(temp[j]);
                System.out.println("-----------------------------------");
            }
            System.out.println("******************************");
        }
    }
  • 相关阅读:
    P1030 求先序排列 P1305 新二叉树
    spfa
    Clairewd’s message ekmp
    Cyclic Nacklace hdu3746 kmp 最小循环节
    P1233 木棍加工 dp LIS
    P1052 过河 线性dp 路径压缩
    Best Reward 拓展kmp
    Period kmp
    Substrings kmp
    Count the string kmp
  • 原文地址:https://www.cnblogs.com/zhouxiaosong/p/6822197.html
Copyright © 2011-2022 走看看