• 回溯算法


    (1)问题描述:子集和问题的一个实例为<data, num>。其中 data = {x1, x2, ......, xn} 是一个正整数的集合,targetValue 是一个正整数。子集和问题判定是否存在 data 的一个子集 data1,使得

    x1 + x2 + ...... + xn = targetValue (x € data1)

    (2)算法设计:使用回溯算法子集树来解决,对于给定的集合 data = {x1, x2, ......, xn} 和正整数 targetValue,计算 data 的一个子集 data1,满足【x1 + x2 + ...... + xn = targetValue (x € data1)】

    (3)算法代码:

    public class SubsetSum {
    
        /**
         * 目标值
         */
        private static Integer targetValue;
    
        /**
         * 当前所选元素之和
         */
        private static Integer sum = 0;
    
        /**
         * 数据个数
         */
        private static Integer num;
    
        /**
         * 未确定值
         */
        private static Integer indeterminacyValue = 0;
    
        /**
         * 数据数组
         */
        private static Integer[] data;
    
        /**
         * 数据存放【0:不存放 1:存放】
         */
        private static Integer[] store;
    
        /**
         * 初始化数据
         */
        private static void initData() {
            Scanner input = new Scanner(System.in);
            System.out.println("请输入目标值:");
            targetValue = input.nextInt();
            
            System.out.println("请输入数据个数:");
            num = input.nextInt();
    
            data = new Integer[num];
            store = new Integer[num];
            System.out.println("请输入各个数:");
            for (int i = 0; i < data.length; i++) {
                data[i] = input.nextInt();
                store[i] = 0;               // 初始化都不存放
                indeterminacyValue += data[i];
            }
        }
    
        /**
         * 回溯查找
         */
        private static Boolean backtrack(int i) {
            if (sum == targetValue) {   // 找到可行解,直接返回 true
                return true;
            }
            if (i == data.length) {     // 找不到可行解,直接返回 false
                return false;
            }
            indeterminacyValue -= data[i];                  // 计算还未确定数的总和
            if (sum + data[i] <= targetValue) {             // 当前 sum + data[i] <= targetValue 直接进入左子树
                store[i] = 1;                               // 数据 i 存放,列入所选加数之中
                sum += data[i];
                if (backtrack(i + 1)) {                  // 继续深入下一层判定求和
                    return true;
                }
                sum -= data[i];                             // 求解深入完毕,若不满足所求的解,需要回溯,恢复当前的 sum 起始值
            }
            if (sum + indeterminacyValue >= targetValue) {  // 剪枝函数【若当前 sum + 未确定的值 >= 目标值,才进入右子树深度搜索;否则没有任何意义】
                store[i] = 0;                               // 数据 i 此时不存放,列入所选加数之中
                if (backtrack(i + 1)) {
                    return true;
                }
            }
            indeterminacyValue += data[i];                  // 求解深入完毕,若不满足所求的解,需要回溯,恢复当前的 indeterminacyValue 起始值
            return false;
        }
    
        /**
         * 输出
         */
        private static void print() {
            System.out.println("
    数据数组:");
            Stream.of(data).forEach(element -> System.out.print(element + " "));
            System.out.println();
            System.out.println("数据存放:");
            Stream.of(store).forEach(element -> System.out.print(element + " "));
            System.out.println();
            System.out.println("组成该目标值的数为:");
            for (int i = 0; i < store.length; i++) {
                if (store[i] == 1) {
                    System.out.print(data[i] + " ");
                }
            }
            System.out.println();
        }
        
        public static void main(String[] args) {
            // 初始化数据
            initData();
    
            // 回溯查找
            backtrack(0);
    
            // 输出
            print();
        }
    
    }
    子集和问题核心代码

    (4)输入输出:

    请输入目标值:
    10
    请输入数据个数:
    5
    请输入各个数:
    2 2 6 5 4
    
    数据数组:
    2 2 6 5 4 
    数据存放:
    1 1 1 0 0 
    组成该目标值的数为:
    2 2 6 
    输入输出

    (5)总结:子集和同样也完全体现了回溯法中子集树的核心思想,时间复杂度 O(2n) ,通过存与不存,判断是否剪枝,进入深度搜索一个解,一旦搜索到一个解直接返回即可;

      建议:若肉眼看不太懂,可以在纸上根据我的代码思路,画一画走一遍求解的流程,便于理解代码,掌握回溯法子集树的核心思想;

  • 相关阅读:
    redis list 清空记录小技巧
    fastJson 之 JSONPath使用
    【移动开发】怎样自己定义ViewGroup
    POJ 3255 Roadblocks
    java带图片的邮件发送方法实现
    android学习二(Activity)
    OpenWRT加入 crontab开机默认运行
    ubuntu14.04启动提示set_sw_state failed
    jekyll bootstrap更改主题theme
    windows API 实现截图
  • 原文地址:https://www.cnblogs.com/blogtech/p/12302565.html
Copyright © 2020-2023  润新知