【分支限界法】任务分配问题(Java)

【分支限界法】任务分配问题(Java)

问题介绍

任务分配问题:将n个任务分配给n个人,每人一个任务,任务不能重复分配,使得总工作代价最小化。

问题分析

 利用分支界限法,求出选择后的下界。例如:
{9, 2, 7, 8},
{6, 4, 3, 7},
{5, 8, 1, 8},
{7, 6, 9, 4}
 上述表的含义为:行是一个人对应工作,列是每个工作对于不同人。第一行9,2, 7, 8则是在针对这个人,第一个工作的工作代价为9,第二个工作的工作代价为2,以此类推。
 在这个问题中,每次求下界,这个下界就是在这个选择以内的最好情况,初始时选择2+3+1+4,哪怕违背了同一个任务不能选择多次的原则,但这绝对是最小的工作代价!所以这就是初始的下界。
 状态空间树如图所示:
在这里插入图片描述

 为第一个人选择任务,固定下一个对象,随后下界改变,例如固定选择9,则后续的第二人选择其他任务,依次下去……按照深度遍历遍历第一人选择9后的情况,得到该情况的下界,将该情况下界和当前的下界比较。
 当我们有更好的下界情况,就对其他部分进行剪枝操作,毕竟,对不好的情况求解只是浪费时间。

代码

 采用分支界限法,使用优先队列来存储待扩展的结点,每次选择具有最小下界估计值的结点进行扩展。在扩展过程中,生成所有可行的子结点,并计算它们的下界估计值。若某个子结点是完整的解,则更新当前的最优解。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.PriorityQueue;

public class Assignment {
    static int[][] cost = {
            {9, 2, 7, 8},
            {6, 4, 3, 7},
            {5, 8, 1, 8},
            {7, 6, 9, 4}
    };
    static int bestSoFar = 9 + 7 + 8 + 9; // 取每一行的最大值相加

    public static void main(String[] args) {
        // 初始化优先队列,备用
        PriorityQueue<Node> S = new PriorityQueue<>();
        // 初始化一个空状态空间树的结点,即根结点
        Node root = new Node();
        // 计算第一个目标估计值
        root.lowerBound = getLowerBound(root);
        // 优先队列中加入根结点,然后准备从根结点开始对状态空间树进行深度优先遍历
        S.add(root);
        while (!S.isEmpty()) {
            Node node = S.remove(); // 贪心策略:取出lowerBound最小的结点
            if (node.lowerBound >= bestSoFar) continue; //剪枝
            for (Node child : allFeasibleChildren(node)) { // 对每一个**可扩展**的子结点
                if (isACompleteSolution(child)) { // 如果这个子结点已经是叶子结点
                    bestSoFar = Math.min(bestSoFar, getLowerBound(child)); // 更新best_so_far
                } else
                    S.add(child); //加入到优先队列中
            }
        }
        System.out.print(bestSoFar);
    }

    private static int getLowerBound(Node node) {
        int lb = 0;
        // 依照node的信息和权值矩阵cost,计算当前结点的下界估计值
        // 1. 估计值 = 已经分配的工作代价+未分配的工作代价
        for (int i = 0; i < node.depth; i++) {
            lb += cost[i][node.partialSolution.get(i)];
        }
        for (int i = node.depth; i < 4; i++) {
            lb += minRemainingCost(cost, node.partialSolution, i);
        }
        return lb;
    }

    private static int minRemainingCost(int[][] cost, HashMap<Integer, Integer> partialSolution, int i) {
        int min = Integer.MAX_VALUE;
        for (int j = 0; j < 4; j++) {
            if (!partialSolution.containsValue(j)) {
                min = Math.min(min, cost[i][j]);
            }
        }
        return min;
    }

    private static Iterable<Node> allFeasibleChildren(Node node) {
        // nodeList是用来返回的可迭代对象,包含参数node的所有**可扩展**子结点
        ArrayList nodeList = new ArrayList();
        // 1. 确保node的深度可以保证进一步扩展子结点
        // Insert your code here
        if (node.depth >= 4) {
            return nodeList;
        }
        // 2. 对所有可能的孩子进行遍历
        for (int i = 0; i < 4; i++) {
            // 如果孩子是可扩展的(即不做重复的,先前别人做过的工作)
            if (feasible(node, i)) {
                // 0. 创建孩子结点:注意维护孩子结点的所有内部属性:
                Node child = new Node();
                // 1. partialSolution
                child.partialSolution = new HashMap<>(node.partialSolution);
                child.partialSolution.put(node.depth, i);
                // 2. depth
                child.depth = node.depth + 1;
                // 3. lowerBound
                child.lowerBound = getLowerBound(child);
                nodeList.add(child);
            }
        }

        return nodeList;
    }

    private static boolean isACompleteSolution(Node n) {
        return n.depth == 4;//检查是否深度遍历完成
    }

    private static boolean feasible(Node n, int i) {
        return !n.partialSolution.containsValue(i);//检查是否被分配
    }
}



import java.util.HashMap;

public class Node implements Comparable<Node>{
    // 实现这个接口是优先队列的要求:队列的元素要能比较大小
// 状态空间树上的后继(子)结点避免和父结点(以及祖先结点)重复做同样的任务。
// 如果每个结点都记录父结点,则可以判定是否和祖先结点做的任务冲突。
// 为了使得查找更容易,这里还是将所有的祖先结点都保存下来了。
// HashMap partialSolution;
    HashMap<Integer, Integer> partialSolution;
    int lowerBound;
    int depth;
    public Node() {
        partialSolution = new HashMap();
        lowerBound = 0;
        depth = 0;
    }
    @Override
    public int compareTo(Node o) {
        // 优先队列中存放的都是Node对象,对象的大小由lowerBound来决定。
        // 如果当前对象小于参数对象o,返回负数;等于参数对象o,返回0;
        // 大于参数对象o,返回正数
        return Integer.compare(this.lowerBound, o.lowerBound);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值