天天看点

动态规划例子,凑硬币,支持各种硬币组合并打印组合详情

凑硬币是动态规划的一个经典例子,比如有硬币1,3,5,则组合出11有几种方案,最少硬币个数的有几种方案。

网上例子一般都是硬币组合1,3,5,如果不是1开头的就挂了,比如硬币组合是2,5,8,自己编写了支持所有硬币组合的代码。

有两个例子:

输出和等于target的最小个数硬币组合

/**
     * 输出和等于target的最小个数硬币组合
     * @param coins 硬币有哪几种,如{1,3,5}
     * @param target 硬币和等于多少,如13
     */
    private static void calcMinCoins(int [] coins, int target)
           

输出和等于target的所有硬币组合

/**
     * 输出和等于target的所有硬币组合
     * @param coins 硬币有哪几种,如{1,3,5}
     * @param target 硬币和等于多少,如13
     */
    private static void calcAllCoins(int [] coins, int target)
           

调用函数

public static void main(String[] args) {
        calcMinCoins(new int[]{2,5,8}, 50);
        calcAllCoins(new int[]{2,5,8}, 50);
    }
           

具体代码如下:

/**
     * 输出和等于target的最小个数硬币组合
     * @param coins 硬币有哪几种,如{1,3,5}
     * @param target 硬币和等于多少,如13
     */
    private static void calcMinCoins(int [] coins, int target) {
        Map map = new HashMap<>();
        for(int i=0;i<=target;i++){
            List listi = (List)map.get(i);//所有和等于i的组合
            if(listi == null){//若没有则初始化
                listi = new ArrayList();
                map.put(i,listi);
            }
            if(i == 0){//所有和等于0的组合是一个空组合
                listi.add(new ArrayList());
            }
            for(int coin : coins){
                if(coin <= i){//如果和等于i,减去coin还大于等于0,则找到和等于tmp
                   int tmp = i - coin;//和等于tmp
                   List list = (List)map.get(tmp);//所有和等于tmp的组合
                   if(list == null){//若没有则初始化
                       list = new ArrayList();
                       map.put(tmp,list);
                   }
                   for(int j=0;j<list.size();j++){//遍历所有和等于tmp 加上coin 就等于i了
                       List subList = (List)list.get(j);
                       List tmp2 = new ArrayList(subList);
                       tmp2.add(coin);
                       //把和等于i的组合加进listi中,前提是不重复,并且个数最小,如果比已存的组合包含元素更少,则把已存组合删了
                       Collections.sort(tmp2);
                       boolean same = false;
                       boolean bigger = false;
                       boolean isMin = false;
                       for(int k=0;k<listi.size();k++){
                           List tmpk = (List)listi.get(k);
                           if(tmpk.size() > tmp2.size()){
                               isMin = true;
                               break;
                           }else{
                               if(JSON.toJSONString(tmpk).equals(JSON.toJSONString(tmp2))){
                                   same = true;
                                   break;
                               }
                               if(tmpk.size() < tmp2.size()){
                                   bigger = true;
                                   break;
                               }
                           }
                       }
                       if(isMin){
                           listi.clear();//如果比已存的组合包含元素更少,则把已存组合删了
                           listi.add(tmp2);//把和等于i的组合加进listi中
                       }else{
                           if(!same&&!bigger){
                               listi.add(tmp2);//把和等于i的组合加进listi中
                           }
                       }
                   }
                }
            }
            //找到最小组合包含几个元素
            int min = 0;
            if(listi.size() > 0){
                min = ((List)listi.get(0)).size();
            }
            System.out.println("所有和等于" + i + "的最小组合包含元素个数" + min+" 这些组合为" + listi);
        }
    }
           

这个代码输出

所有和等于0的最小组合包含元素个数0 这些组合为[[]]
所有和等于1的最小组合包含元素个数0 这些组合为[]
所有和等于2的最小组合包含元素个数1 这些组合为[[2]]
所有和等于3的最小组合包含元素个数0 这些组合为[]
所有和等于4的最小组合包含元素个数2 这些组合为[[2, 2]]
所有和等于5的最小组合包含元素个数1 这些组合为[[5]]
所有和等于6的最小组合包含元素个数3 这些组合为[[2, 2, 2]]
所有和等于7的最小组合包含元素个数2 这些组合为[[2, 5]]
所有和等于8的最小组合包含元素个数1 这些组合为[[8]]
所有和等于9的最小组合包含元素个数3 这些组合为[[2, 2, 5]]
所有和等于10的最小组合包含元素个数2 这些组合为[[2, 8], [5, 5]]
所有和等于11的最小组合包含元素个数4 这些组合为[[2, 2, 2, 5]]
所有和等于12的最小组合包含元素个数3 这些组合为[[2, 2, 8], [2, 5, 5]]
所有和等于13的最小组合包含元素个数2 这些组合为[[5, 8]]
所有和等于14的最小组合包含元素个数4 这些组合为[[2, 2, 2, 8], [2, 2, 5, 5]]
所有和等于15的最小组合包含元素个数3 这些组合为[[2, 5, 8], [5, 5, 5]]
所有和等于16的最小组合包含元素个数2 这些组合为[[8, 8]]
所有和等于17的最小组合包含元素个数4 这些组合为[[2, 2, 5, 8], [2, 5, 5, 5]]
所有和等于18的最小组合包含元素个数3 这些组合为[[2, 8, 8], [5, 5, 8]]
所有和等于19的最小组合包含元素个数5 这些组合为[[2, 2, 2, 5, 8], [2, 2, 5, 5, 5]]
所有和等于20的最小组合包含元素个数4 这些组合为[[2, 2, 8, 8], [2, 5, 5, 8], [5, 5, 5, 5]]。。。后略
           
/**
     * 输出和等于target的所有硬币组合
     * @param coins 硬币有哪几种,如{1,3,5}
     * @param target 硬币和等于多少,如13
     */
    private static void calcAllCoins(int [] coins, int target) {
        Map map = new HashMap<>();
        for(int i=0;i<=target;i++){
            List listi = (List)map.get(i);//所有和等于i的组合
            if(listi == null){//若没有则初始化
                listi = new ArrayList();
                map.put(i,listi);
            }
            if(i == 0){//所有和等于0的组合是一个空组合
                listi.add(new ArrayList());
            }
            for(int coin : coins){
                if(coin <= i){//如果和等于i,减去coin还大于等于0,则找到和等于tmp
                    int tmp = i - coin;//和等于tmp
                    List list = (List)map.get(tmp);//所有和等于tmp的组合
                    if(list == null){//若没有则初始化
                        list = new ArrayList();
                        map.put(tmp,list);
                    }
                    for(int j=0;j<list.size();j++){//遍历所有和等于tmp 加上coin 就等于i了
                        List subList = (List)list.get(j);
                        List tmp2 = new ArrayList(subList);
                        tmp2.add(coin);
                        //把和等于i的组合加进listi中,前提是不重复
                        Collections.sort(tmp2);
                        boolean same = false;
                        for(int k=0;k<listi.size();k++){
                            List tmpk = (List)listi.get(k);
                            if(JSON.toJSONString(tmpk).equals(JSON.toJSONString(tmp2))){
                                same = true;
                                break;
                            }
                        }
                        if(!same){
                            listi.add(tmp2);//把和等于i的组合加进listi中
                        }
                    }
                }
            }
            System.out.println("所有和等于" + i + "的组合为" + listi);
        }
    }
           

这个代码输出

所有和等于0的组合为[[]]
所有和等于1的组合为[]
所有和等于2的组合为[[2]]
所有和等于3的组合为[]
所有和等于4的组合为[[2, 2]]
所有和等于5的组合为[[5]]
所有和等于6的组合为[[2, 2, 2]]
所有和等于7的组合为[[2, 5]]
所有和等于8的组合为[[2, 2, 2, 2], [8]]
所有和等于9的组合为[[2, 2, 5]]
所有和等于10的组合为[[2, 2, 2, 2, 2], [2, 8], [5, 5]]
所有和等于11的组合为[[2, 2, 2, 5]]
所有和等于12的组合为[[2, 2, 2, 2, 2, 2], [2, 2, 8], [2, 5, 5]]
所有和等于13的组合为[[2, 2, 2, 2, 5], [5, 8]]
所有和等于14的组合为[[2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 8], [2, 2, 5, 5]]
所有和等于15的组合为[[2, 2, 2, 2, 2, 5], [2, 5, 8], [5, 5, 5]]
所有和等于16的组合为[[2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 8], [2, 2, 2, 5, 5], [8, 8]]
所有和等于17的组合为[[2, 2, 2, 2, 2, 2, 5], [2, 2, 5, 8], [2, 5, 5, 5]]
所有和等于18的组合为[[2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 8], [2, 2, 2, 2, 5, 5], [2, 8, 8], [5, 5, 8]]
所有和等于19的组合为[[2, 2, 2, 2, 2, 2, 2, 5], [2, 2, 2, 5, 8], [2, 2, 5, 5, 5]]
所有和等于20的组合为[[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 8], [2, 2, 2, 2, 2, 5, 5], [2, 2, 8, 8], [2, 5, 5, 8], [5, 5, 5, 5]]
           

继续阅读