首页 > 试题广场 >

订单分配

[编程题]订单分配
  • 热度指数:2001 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在N*N的矩阵A 其中Aij 代表订单i司机j匹配的分值。

假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。


输入描述:

第一行包含一个整数N,2≤N≤10。

第二行至第N+1行包含N*N的矩阵。



输出描述:
输出分值累加结果和匹配列表,结果四舍五入保留小数点后两位
(注意如果有多组派单方式得到的结果相同,则有限为编号小的司机分配编号小的订单,比如:司机1得到1号单,司机2得到2号单,就比司机1得到2号单,司机2得到1号单要好)
示例1

输入

3
1.08 1.25 1.5
1.5 1.35  1.75
1.22 1.48 2.5

输出

5.25
1 2
2 1
3 3

说明

第一行代表得到的最大分值累加结果5.25,四舍五入保留两位小数;

第二行至第四行代表匹配的结果[i j],其中i按行递增:

订单1被派给司机2,订单2被派给司机1,订单3被派给司机3。使得A12+ A21+ A33= 1.25 + 1.5 + 2.5 = 5.25在所有的组合中最大。

import java.io.*;
/**
 * @Description: 计算并保存所有分组方式下的 score 值,取最大值 优化方向 -> 循环计算并 始终存储一组最好的值
 * @Param:
 * @return:
 * @Author: xulifeng
 * @Date: 3/10/2022
 */
public class Main {
    // 原始分配表
    static double[][] score;
    // 最终分配结果
    static double[][] bestPath;
    // 当前分配方式
    static double[][] curPath;
    // 最大score
    static double maxScore = 0;
    // 订单数
    static int orderNum;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        orderNum = Integer.parseInt(br.readLine());
        score = new double[orderNum][orderNum];
        bestPath = new double[orderNum][orderNum];
        curPath = new double[orderNum][orderNum];
        // 填充score、bestPath、curPath
        for (int i = 0; i < orderNum; i++) {
            String[] oneRowScore = br.readLine().split(" ");
            for (int j = 0; j < orderNum; j++) {
                score[i][j] = Double.parseDouble(oneRowScore[j]);
                bestPath[i][j] = 0;
                curPath[i][j] = 0;
            }
        }
        //从第0行开始递归
        backTracing(0, orderNum);
        System.out.printf("%.2f",maxScore);
        System.out.println();

        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                if (bestPath[i][j] != 0) {
                    // +1是因为订单 车是从1开始 而数组从零开始
                    System.out.print(i+1);
                    System.out.print(" ");
                    System.out.println(j+1);
                }
            }
        }
    }
    /**
     * @Description: 回溯算法 检查这个位置是否还能存放
     * @Param: 当前行curRow 总列数
     * @return:
     * @Author: xulifeng
     * @Date: 3/9/2022
     */
    public static void backTracing(int curRow, int totalCol) {
        // 遍历完所有行时 row 和 col 大小一样
        if (curRow == orderNum) {
            // 将深拷贝的数据添加进result 浅拷贝会因为回溯全部变为0
            double curScore = computeCurScore();
            if (curScore > maxScore) {
                maxScore = curScore;
                bestPath = deepCopy();
            }
            return;
        }
        //index 其实也为行 从上往下判断
        for (int index = 0; index < totalCol; index++) {
            if (isValid(curRow, index)) {
                // 可以分配订单
                curPath[curRow][index] = score[curRow][index];
                backTracing(curRow+1,totalCol);
                //撤销当前的操作 因为撤销操作 所有的bestPath数组最终都变成了0回溯不可避免的事情
                curPath[curRow][index] = 0;
            }
        }
    }
    // 检查目前是否能在 row col这个位置存放数据 要求同一列只能有一个数据
    public static boolean isValid(int curRow, int curCol){
        //检查[curRow][curCol]这一列之前是否已经有数据
        for (int i = 0; i < curRow; i++) {
            if (curPath[i][curCol] != 0) {
                return false;
            }
        }
        return true;
    }
    // 计算当前分配方式下的score
    public static double computeCurScore() {
        double score = 0;
        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                score += curPath[i][j];
            }
        }
        return score;
    }
    // 深拷贝
    public static double[][] deepCopy() {
        double[][] temp = new double[orderNum][orderNum];
        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                temp[i][j] = curPath[i][j];
            }
        }
        return temp;
    }
}
发表于 2022-03-10 11:29:44 回复(1)