回溯法与分支限界法题目分享

  1. 组合问题
package Retrospective;

import java.util.*;

/**
 * @ClassName: Combination
 * @Description: TODO
 * @Attention: 类名使用UpperCamelCase风格,
 * 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格
 * 测试方法名、常量、枚举名称使用snake_case风格,测试方法小写、常量、枚举大写
 * @author: pxy
 * @date: 2022/5/26  9:29
 *
 * 【问题描述】给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字仅能被选取1次。
 * 【样例输入】
 * 2 1 3 6 5 7
 * 6
 * 【样例输出】
 * 1 2 3
 * 1 5
 * 6
 * 【样例说明】输入第一行为数组candidates,第二行为目标数target。输出为所有使数字和为 target 的组合。
 */
public class Combination {
    static LinkedList<Integer> list = new LinkedList<>();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int[] arr = Arrays.stream(s.split(" ")).mapToInt(Integer::parseInt).toArray();
        int n = sc.nextInt();
        //为了减少搜寻次数对数组排序
        Arrays.sort(arr);
        combination(n, arr);
    }


    static void combination(int n, int[] arr) {
        backtracking(n, arr, 0, 0);
    }

    static void backtracking(int n, int[] arr, int startIndex, int sum) {
        if (sum == n) {
            for (Integer list : list) {
                System.out.print(list + " ");
            }
            System.out.println();
            return;
        }
        if (sum > n) return;

        for (int i = startIndex; i < arr.length; i++) {
            list.add(arr[i]);
            sum += arr[i];
            backtracking(n, arr, i + 1, sum);
            list.removeLast();
            sum -= arr[i];
        }
    }
}

  1. N皇后问题
package Retrospective;

import java.util.Scanner;

/**
 * @ClassName: NQueen
 * @Description: TODO
 * @Attention: 类名使用UpperCamelCase风格,
 * 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格
 * 测试方法名、常量、枚举名称使用snake_case风格,测试方法小写、常量、枚举大写
 * @author: pxy
 * @date: 2022/5/26  10:52
 *
 * 【问题描述】n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
 * 【样例输入】4
 * 【样例输出】2
 * 【样例说明】输入为皇后的个数,输出为问题的解法,4皇后问题有两个不同的解法,即[2,4,1,3]和[3,1,4,2](数组第i个元素表示第i个皇后放在第i行的位置)
 */
public class NQueen {
    //解法数量
    static int num = 0;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int queenNumber = sc.nextInt();
        nQueen(queenNumber);
    }

    static void nQueen(int queenNumber) {
        boolean[][] flag = new boolean[queenNumber][queenNumber];
        backtracking(queenNumber, 0, flag);
        System.out.println(num);

    }

    static void backtracking(int n, int row, boolean[][] flag) {
        if (row == n) {
            num++;
            return;
        }
        for (int col = 0; col < n; col++) {
            if (checkValid(row, col, n, flag)) {
                flag[row][col] = true;
                backtracking(n, row + 1, flag);
                flag[row][col] = false;
            }

        }

    }

    static boolean checkValid(int row, int col, int n, boolean[][] flag) {
        //同列
        for (int i = 0; i < row; i++) {
            if (flag[i][col]) return false;
        }
        //对角线
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (flag[i][j]) return false;
        }
        //对角线
        for (int i = row - 1, j = col + 1; i >= 0 && j <= n - 1; i--, j++) {
            if (flag[i][j]) return false;
        }

        return true;
    }
}
  1. 分支限界法解决背包问题
package BranchAndLimit;

import java.util.*;

/**
 * @ClassName: pack
 * @Description: TODO
 * @Attention: 类名使用UpperCamelCase风格,
 * 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格
 * 测试方法名、常量、枚举名称使用snake_case风格,测试方法小写、常量、枚举大写
 * @author: pxy
 * @date: 2022/5/27  11:28
 *
 *
 * 给定n个重量为wi,价值为vi的物品(i=1,2,…,n),以及一个重量为W的背包,找出其中最有价值的物品子集,并且能全部放入背包中。
 * 【样例输入】
 * 10
 * 4
 * 7 42
 * 3 12
 * 4 40
 * 5 25
 * 【样例输出】
 * 65
 * 【样例说明】输入第一行是背包重量上限,第二行是物品数量,之后是每个物品的重量和价值。输出为能放入背包的物品最大价值
 */
public class pack {
    /**
     * 总容量
     */
    private int capacity;

    /**
     * 总数量
     */
    private int number;

    public BinaryTree binaryTree;

    /**
     * 初始化
     */
    public void initialization() {
        // 建立根节点
        Node head = new Node();
        head.setWeight(0);
        head.setValue(0);
        head.setLayer(0);
        head.setCurrentSolution("");
        // 根据每单位质量价值来从大到小排序
        binaryTree.getItemInfoList().sort((o1, o2) -> {
            Double a = (double) (o1.getValue() / o1.getWeight());
            Double b = (double) (o2.getValue() / o2.getWeight());
            return b.compareTo(a);
        });
        ItemInfo firstNode = binaryTree.getItemInfoList().get(0);
        head.setUp(capacity * (firstNode.getValue() * 1.0 / firstNode.getWeight()));
        binaryTree.setBackpack(this);
        // 建立树
        binaryTree.createdBinaryTree(head, 0);
        binaryTree.branchBound(head);
    }

    public void input() {
        Scanner scanner = new Scanner(System.in);
        // 背包容量
        capacity = scanner.nextInt();
        // 物品数量
        number = scanner.nextInt();
        binaryTree.setItemInfoList(new ArrayList<>(number));
        for (int i = 0; i < number; i++) {
            int weight = scanner.nextInt();
            int value = scanner.nextInt();
            ItemInfo itemInfo = new ItemInfo(weight, value);
            binaryTree.getItemInfoList().add(itemInfo);
        }
    }

    public int getCapacity() {
        return capacity;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }


    public static void main(String[] args) {
        pack backpack = new pack();
        backpack.binaryTree = new BinaryTree();
        backpack.input();
        backpack.initialization();
        //最大价值
        System.out.println(backpack.binaryTree.getMax());
        String bestSolution = backpack.binaryTree.getBestSolution();
        /**
         * 拿取的物品
         */
        for (int i = 0; i < bestSolution.length(); i++) {
            if (bestSolution.charAt(i) != '0') {
                ItemInfo itemInfo = backpack.binaryTree.getItemInfoList().get(i);
                System.out.println("[" + itemInfo.getWeight() + " " + itemInfo.getValue() + "]");
            }
        }
    }
}

/**
 * 物品基本信息
 */
class ItemInfo {

    /**
     * 重量
     */
    private int weight;

    /**
     * 价值
     */
    private int value;

    public ItemInfo() {
    }

    public ItemInfo(int weight, int value) {
        this.weight = weight;
        this.value = value;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

class Node extends ItemInfo implements Comparable {

    /**
     * 层级,用来判断是否为叶子节点
     */
    private int layer;

    /**
     * 当前解,算出最大价值后,方便找出拿了哪些物品
     */
    private String currentSolution;

    /**
     * 上限
     */
    private double up;

    /**
     * 取
     */
    private Node left;

    /**
     * 不取
     */
    private Node right;

    public Node() {
    }

    public int getLayer() {
        return layer;
    }

    public void setLayer(int layer) {
        this.layer = layer;
    }

    public String getCurrentSolution() {
        return currentSolution;
    }

    public void setCurrentSolution(String currentSolution) {
        this.currentSolution = currentSolution;
    }

    public double getUp() {
        return up;
    }

    public void setUp(double up) {
        this.up = up;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    /**
     * 优先队列排列方式,根据up值大小来排序 * @param o Node * @return int
     */
    @Override
    public int compareTo(Object o) {
        Node node = (Node) o;
        if (node.getUp() > this.getUp()) {
            return 1;
        } else if (node.getUp() == this.getUp()) {
            return 0;
        }
        return -1;
    }

}

class BinaryTree {

    /**
     * 保存全部物品的基本信息,根据每单位质量价值从大到小来排序
     */
    private List<ItemInfo> itemInfoList;

    /**
     * 最优解(假如为:1010,表明第一个拿,第二个不拿,第三个拿,第四个不拿)
     */
    private String bestSolution;

    /**
     * 最优值
     */
    private int max;

    private pack backpack;

    /**
     * 判断节点是否符合要求 * @param node 树的当前节点 * @param upPriorityQueue 优先队列
     */
    private void judgementNode(Node node, Queue<Node> upPriorityQueue) {
        if (node == null) {
            return;
        }
        // 当前节点对应的总重量超过背包最大容量 || 当前节点最可能的最大价值up小于已知的价值
        // node.getWeight() > backpack.getCapacity()这个判断实际上是多余的。。
        if (node.getWeight() > backpack.getCapacity() || node.getUp() < max) {
            return;
        }
        // 当前的节点对应的层级是否小于总数量
        if (node.getLayer() < backpack.getNumber()) {
            upPriorityQueue.add(node);
        } else {
            // 更新最优价值
            max = node.getValue();
            // 更新最优解
            bestSolution = node.getCurrentSolution();
        }
    }

    /**
     * 分支界定 * @param node 根节点
     */
    public void branchBound(Node node) {
        // 使用优先队列,根据节点的up值,优先权由大到小
        Queue<Node> upPriorityQueue = new PriorityQueue<>();
        upPriorityQueue.add(node);
        while (!upPriorityQueue.isEmpty()) {
            node = upPriorityQueue.poll();
            // 若是队列中第一个节点的up值小于最优价值,则就不必继续遍历队列。
            if (node.getUp() <= max) {
                break;
            }
            judgementNode(node.getLeft(), upPriorityQueue);
            judgementNode(node.getRight(), upPriorityQueue);
        }
    }

    /**
     * 建立二叉树 * @param node 当前节点 * @param index 物品List对应下标
     */
    public void createdBinaryTree(Node node, int index) {
        if (index >= backpack.getNumber()) {
            return;
        }
        // 获取当前物品信息
        ItemInfo currentItem = itemInfoList.get(index);
        if (node.getLeft() == null) {
            Node leftNode = new Node();
            // 由于是左节点,表明拿。被拿当前物品价值+已有的物品总价值
            leftNode.setValue(currentItem.getValue() + node.getValue());
            // 被拿当前物品重量+已有的物品总重量
            leftNode.setWeight(currentItem.getWeight() + node.getWeight());
            // 设置层级index从0开始,0表明根节点的层级
            leftNode.setLayer(index + 1);
            // 算出剩余容量
            int surplus = backpack.getCapacity() - leftNode.getWeight();
            // 这里进行减去枝,若是剩余容量小于0说明当前物品没法放入
            if (surplus >= 0) {
                // 若是不是最后一层,index在这里,能够理解为层级
                if (index + 1 < itemInfoList.size()) {
                    // 当前物品的下一个物品
                    ItemInfo nextNode = itemInfoList.get(index + 1);
                    // 求出剩余物品的最大up值,下一个物品每单位容量最大价值*剩余容量
                    int up = surplus * (nextNode.getValue() / nextNode.getWeight());
                    // 是否为根节点
                    if (index == 0) {
                        // 根节点只须要获取当前物品的价值+up
                        leftNode.setUp(up + currentItem.getValue());
                    } else {
                        // 非根节点须要获取当前总价值+up
                        leftNode.setUp(up + leftNode.getValue());
                    }
                } else {
                    // 最后一层,其实就是最后一个物品,对应最后一层的节点的up就是当前节点的value
                    leftNode.setUp(node.getValue());
                }
                // 更新解过程,1表明left,即拿取
                leftNode.setCurrentSolution(node.getCurrentSolution() + 1);
                node.setLeft(leftNode);
                createdBinaryTree(node.getLeft(), index + 1);
            }
        }
        if (node.getRight() == null) {
            Node rightNode = new Node();
            // 由于是右节点,表明不拿。因此直接设置为已有的物品总价值
            rightNode.setValue(node.getValue());
            rightNode.setWeight(node.getWeight());
            rightNode.setLayer(index + 1);
            int surplus = backpack.getCapacity() - rightNode.getWeight();
            if (surplus >= 0) {
                if (index + 1 < itemInfoList.size()) {
                    ItemInfo nextNode = itemInfoList.get(index + 1);
                    int up = surplus * (nextNode.getValue() / nextNode.getWeight());
                    if (index == 0) {
                        rightNode.setUp(up);
                    } else {
                        rightNode.setUp(up + rightNode.getValue());
                    }
                } else {
                    rightNode.setUp(node.getValue());
                }
                rightNode.setCurrentSolution(node.getCurrentSolution() + 0);
                node.setRight(rightNode);
                createdBinaryTree(node.getRight(), index + 1);
            }
        }
    }

    /**
     * 遍历树 * @param node 节点
     */
    public void preOrderBinaryTree(Node node) {
        if (node != null) {
            System.out.println(node.toString());
            preOrderBinaryTree(node.getLeft());
            preOrderBinaryTree(node.getRight());
        }
    }

    public List<ItemInfo> getItemInfoList() {
        return itemInfoList;
    }

    public void setItemInfoList(List<ItemInfo> itemInfoList) {
        this.itemInfoList = itemInfoList;
    }

    public String getBestSolution() {
        return bestSolution;
    }

    public void setBestSolution(String bestSolution) {
        this.bestSolution = bestSolution;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public pack getBackpack() {
        return backpack;
    }

    public void setBackpack(pack backpack) {
        this.backpack = backpack;
    }
}
  1. 作业调度问题
package BranchAndLimit;

import java.util.Scanner;

/**
 * @ClassName: OperationScheduling
 * @Description: TODO
 * @Attention: 类名使用UpperCamelCase风格,
 * 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格
 * 测试方法名、常量、枚举名称使用snake_case风格,测试方法小写、常量、枚举大写
 * @author: pxy
 * @date: 2022/5/27  12:15
 *
 * 【问题描述】
 * 假设有 n 个任务由 k 个可并行工作的机器来完成(k<n)。完成任务 i 需要时间为ti ,设计完成这 n 个任务的最佳调度算法,使得完成全部任务的时间最早。
 * 【样例输入】
 * 10
 * 7
 * 67 45 80 32 59 95 37 46 28 20
 * 【样例输出】
 * 95
 * 【样例说明】输入第一行为任务数,第二行为可并行的机器数,第三行为机器完成任务i所需的单位时间,输出为完成所有任务的最优化时间
 */
public class OperationScheduling {
    static int minTime = Integer.MAX_VALUE;
    static int n; //任务数
    static int machine; //可并行的机器数
    static int[] time; //每个任务所需的时间
    static int[] everyOperationTime; //每个机器执行任务所需的时间

    public static void main(String[] args) {
        input();
        backTracking(0, n, machine, time, everyOperationTime);
        System.out.println(minTime);
    }

    static void backTracking(int next, int n, int machine, int[] time, int[] eveOpeTime) {
        if (next == n) {
            int maxTime = solve(machine, eveOpeTime);
            if (maxTime < minTime) minTime = maxTime;
        } else {
            for (int i = 0; i < machine; i++) {
                eveOpeTime[i] = eveOpeTime[i] + time[next];
                if (eveOpeTime[i] < minTime) backTracking(next + 1, n, machine, time, eveOpeTime);
                eveOpeTime[i] = eveOpeTime[i] - time[next];
            }
        }
    }

    static int solve(int machine, int[] eveOpeTime) {
        int maxTime = 0;
        for (int i = 0; i < machine; i++) {
            if (eveOpeTime[i] > maxTime) maxTime = eveOpeTime[i];
        }
        return maxTime;
    }

    static void input() {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        machine = sc.nextInt();
        time = new int[n];
        everyOperationTime = new int[n];
        for (int i = 0; i < n; i++) {
            time[i] = sc.nextInt();
            everyOperationTime[i] = 0;
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值