用广度优先方式来实现分支限界搜索排列树:输出计算最小花费的过程以及最小花费时间

解题思路:
1.确定上下界,下界down是每一行最小值,上界up使用贪心法计算,所以范围为[down,up]
2.限界函数lb=已分配任务员工花费总时间+未分配任务员工最小花费时间
3.使用广度优先遍历,计算lb,如果lb超过上界,则丢弃,如果在范围内,先将第一层数据符合条件的放入优先队列
4.循环队列,每次从优先队列中选取最小的lb进行广度优先遍历
1)如果取出的结点为叶子结点,与min_result比较得到在最终的min_result
2)如果取出的结点为非叶子结点,则访问该结点的孩子节点并计算其lb,如果lb超过上界,则丢弃,如果在范围内,先将孩子节点放入优先队列
循环直到计算完所有值。

运行过程说明:
输入:你选择的文件编号
输出:输出计算最小花费的过程以及最小花费时间

伪码描述:
arrangeWork()
        for j<-0 to n do//使用广度优先遍历,先将第一层加入优先队列,让优先队列不为空(计算lb,如果lb超过上界,则丢弃,如果在范围内,放入优先队列中)。
            node.i<-0
            node.j<-j
            node.v<-this.cost[0][j]
            getLb(node)
            if this.down<=node.lb&&node.lb<=this.up
            then this.PT.add(node)

        }
        while !this.PT.isEmpty() do//每次从中选取最小的lb进行广度优先遍历,直到计算完所有值,从二维数组第二行开始
            node<-this.PT.poll()
            if node.i==this.n-1//即到达叶子结点,记录最小值
                then if this.min_result==0
                then this.min_result<-node.lb
                    this.min_node<-node
                else do
                    if node.lb<this.min_result
                    then this.min_result<-node.lb
                         this.min_node<-node
            else do//不是叶子结点,将该结点的子结点加入优先队列
                k<-node.i+1//层数
                father=node
                for j<-0 to n do//标记全部初始化为0
                    this.flag[j]<-0
                this.flag[node.j]<-1//标记该任务已经被分配
                while node.father!=null do//记录该结点的父结点已经被分配了的任务
                    this.flag[node.father.j]<-1
                    node<-node.father
                for j<-0 to n do//创建孩子结点加入优先队列
                    if this.flag[j]==0
                    then child<-new Node()
                        child.i<-k
                        child.j<-j
                        child.v<-this.cost[k][j]
                        child.father<-father
                        getLb(child)
                        if this.down<=child.lb&&child.lb<=this.up
                        then this.PT.add(child)
                       
算法的复杂度分析:O(n!)
算法设计:
用广度优先方式来实现分支限界搜索排列树
使用优先队列存放满足lb目标函数的结点,将其从小到大排序,循环队列,每次从优先队列中选取最小的lb进行广度优先遍历

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

class Node{
    int i;//记录人员编号方便计算其余下行的最小值
    int j;//记录任务编号,方便标记该任务是否被分配
    int v;//记录当前分配任务员工花费的时间
    int lb;//记录当前分配任务员工目标函数lb=已分配任务员工花费总时间+未分配任务员工最小花费时间
    Node father=null;//方便找到该结点的父结点,用于计算上层结点花费的时间
}

public class assign05 {
    public int n;//n表示任务数或者人数
    public int flag[];//用flag[j] 标记第j个工作是否己分配工,1表示已经分配了工作,0表示还没有分配工作
    public int cost[][];//记录不同人完成不同任务所需时间
    public int down;//下界:每行最小值之和
    public int up;//上界:使用贪心算法,不同行不同列取值
    public int min[];//记录每行最小值
    public PriorityQueue<Node> PT;//待处理的结点表
    public int min_result;//记录最小的花费时间
    public Node min_node;

    public static void main(String[] args) throws IOException {

        assign05 object=new assign05();
        while (true) {//循环访问不同文件内容
            object.min_result=0;
            object.down= 0;
            object.up= 0;
            object.min_node=null;
            object.readFile();
            object.min=new int[object.n];//初始化min数组
            for (int k=0;k<object.n;k++) {
                object.min[k] = (int) Double.POSITIVE_INFINITY;
            }
            object.getDown();
            System.out.println("该二维数组的下界为:"+object.down);
            object.getUp();
            System.out.println("该二维数组的上界为:"+object.up);
            object.PT=new PriorityQueue<Node>(new Comparator<Node>() {
                @Override
                public int compare(Node o1, Node o2) {
                    return o1.lb-o2.lb;
                }
            });
            object.arrangeWork();
            for (int k=0;k<object.n;k++){
                int i=object.min_node.i;
                int j=object.min_node.j;
                System.out.println("要想花费最少时间应该选择第"+(i+1)+"人,完成第"+(j+1)+"份任务");
                object.min_node=object.min_node.father;
            }

            System.out.println("将" + object.n + "份作业分配给" + object.n + "个人完成,总花费时间的最短为:" + object.min_result);
        }
    }

    /*读文件,将文件内容写到二维数组中*/
    public void readFile() throws IOException {
        System.out.println("共6组文件(1-6),输入你选择的文件编号:");
        Scanner sc=new Scanner(System.in);
        int fileNum=sc.nextInt();//获得文件编号
        BufferedReader bufferedReader=new BufferedReader(new FileReader("./src/input_assign05_0"+fileNum+".txt"));
        this.n=Integer.parseInt(bufferedReader.readLine());//n为n份工作或n个人
        this.cost=new int[this.n][this.n];
        for (int i=0;i<this.n;i++){
            for (int j=0;j<this.n;j++){
                this.cost[i][j]= 0;//初始化
            }
        }
        for (int i=0;i<this.n;i++){
            String str=bufferedReader.readLine();
            for (int j=0;j<this.n;j++){
                this.cost[i][j]= Integer.parseInt(str.split(" ")[j]);//将读取的信息存到cost二维数组中
            }
        }
        this.flag=new int[this.n];
        for (int j=0;j<this.n;j++){//标记该工作是否被分配,初始化为0为被分配,1表示被分配了不能再分配给他人使用
            this.flag[j]=0;
        }
    }

    /*获得down值*/
    public void getDown(){//获得初始下界
        for (int i=0;i<this.n;i++){
            for (int j=0;j<this.n;j++){
                if (this.cost[i][j]<this.min[i]){
                    this.min[i]=this.cost[i][j];//min数组中记录了每行的最小值
                }
            }
            this.down+=this.min[i];//down为每行最小值之和
        }
    }

    /*获得up值*/
    public void getUp(){//获得初始上界
        int loc=-1;
        for (int i=0;i<this.n;i++){
            int min=(int) Double.POSITIVE_INFINITY;//记录每行最小值,初始化为正无穷
            for (int j=0;j<this.n;j++){
                if (cost[i][j]<min&&this.flag[j]==0){
                    min=cost[i][j];
                    loc=j;
                }
            }
            this.flag[loc]=1;
            this.up+=min;
        }
        for (int j=0;j<this.n;j++){//重新将该数组标记置为0,标记该工作是否被分配,0为未被分配,1表示被分配了不能再分配给他人使用
            this.flag[j]=0;
        }
    }

    /*计算结点的lb目标函数*/
    public void getLb(Node node){//lb=已分配任务员工花费总时间+未分配任务员工最小花费时间
        int sum=0;//该结点上层结点花费时间总和
        Node father=node.father;
        while (father!=null){//已分配任务标记为1
            int i=father.i;
            int j=father.j;
            sum+=this.cost[i][j];
            father=father.father;
        }
        int remain_min=0;//记录未分配任务员工最小花费时间
        for (int i=node.i+1;i<this.n;i++){
            remain_min+=this.min[i];//记录未分配任务员工最小花费时间=剩余每行最小值之和,min数组中存放每行最小值
        }
        node.lb=sum+node.v+remain_min;
    }

    /*进行任务分配*/
    public void arrangeWork(){
        for (int j=0;j<this.n;j++){//使用广度优先遍历,先将第一层加入优先队列,让优先队列不为空(计算lb,如果lb超过上界,则丢弃,如果在范围内,放入优先队列中)。
            Node node=new Node();
            node.i=0;
            node.j=j;
            node.v=this.cost[0][j];
            getLb(node);
            if (this.down<=node.lb&&node.lb<=this.up) {
                this.PT.add(node);
            }
        }
        while(!this.PT.isEmpty()){//每次从中选取最小的lb进行广度优先遍历,直到计算完所有值,从二维数组第二行开始
            Node node=this.PT.poll();
            if (node.i==this.n-1){//即到达叶子结点,记录最小值
                if (this.min_result==0){
                    this.min_result=node.lb;
                    this.min_node=node;
                }else {
                    if (node.lb<this.min_result){
                        this.min_result=node.lb;
                        this.min_node=node;
                    }
                }
            } else {//不是叶子结点,将该结点的子结点加入优先队列
                int k=node.i+1;//层数
                Node father=node;
                for (int j=0;j<this.n;j++){//标记全部初始化为0
                    this.flag[j]=0;
                }
                this.flag[node.j]=1;//标记该任务已经被分配
                while (node.father!=null){//记录该结点的父结点已经被分配了的任务
                    this.flag[node.father.j]=1;
                    node=node.father;
                }
                for (int j=0;j<this.n;j++){//创建孩子结点加入优先队列
                    if (this.flag[j]==0){
                        Node child=new Node();
                        child.i=k;
                        child.j=j;
                        child.v=this.cost[k][j];
                        child.father=father;
                        getLb(child);
                        if (this.down<=child.lb&&child.lb<=this.up) {
                            this.PT.add(child);
                        }
                    }
                }
            }
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值