带时间窗口的取送问题 (PDPTW)

解决 带时间窗口的取送问题 (PDPTW),即在特定时间段内,用同一辆车完成所有货物的取货和送货。目标是 减少完成所有订单所需的总工时。

换句话说,货物必须由一辆车从一个地方收集并运送到另一个地方。显然,还存在排序或优先级约束,以确保在交付站点之前访问收集站点。

此外,还需要考虑司机的休息时间,主要为:

  • 每天最多驾驶时间:9小时
  • 4.5小时 驾驶后,需休息 45分钟(可以拆分为15分钟 + 30分钟)。
  • 6小时 工作后,需休息至少 15分钟,工作6到9小时需 30分钟 休息。
其中,最大连续行驶时间 :4.5 小时;每日最大驾驶时间 :9 小时;最长路线时间 :13 小时。
在这里使用贪心算法作为核心,列举一下核心部分的代码(Java):
 
 
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
......

private static void parseInputData(JSONObject inputData) {
    // Parsing order, vehicle and matrix data
    JSONArray ordersData = inputData.getJSONArray("Orders");
    JSONArray vehiclesData = inputData.getJSONArray("Vehicles");
    JSONObject matrixData = inputData.getJSONObject("Matrix");
    JSONArray locationsData = matrixData.getJSONArray("Locations");
    int numLocations = locationsData.length();
    int[][] timeMatrix = new int[numLocations][numLocations];
    int[][] distanceMatrix = new int[numLocations][numLocations];

    // Initialize the time and distance matrix with the diagonal set to 0 and the rest set to Integer.MAX_VALUE
    for (int i = 0; i < numLocations; i++) {
        for (int j = 0; j < numLocations; j++) {
            if (i == j) {
                timeMatrix[i][j] = 0;
                distanceMatrix[i][j] = 0;
            } else {
                timeMatrix[i][j] = Integer.MAX_VALUE;
                distanceMatrix[i][j] = Integer.MAX_VALUE;
            }
        }
    }
    // Parse Matrix.Data
    JSONArray dataOuter = matrixData.getJSONArray("Data"); // outer array
    if (dataOuter.length() > 0) {
        JSONArray data = dataOuter.getJSONArray(0); // Get 2D matrix
        for (int i = 0; i < data.length(); i++) {
            JSONArray row = data.getJSONArray(i);
            for (int j = 0; j < row.length(); j++) {
                if (i == j) {
                    continue;
                }

                JSONArray cell = row.optJSONArray(j); // Use optJSONArray in case it's null.
                if (cell == null || cell.isNull(0)) {
                    // Keep Integer.MAX_VALUE
                    continue;
                } else {
                    if (cell.length() >= 2) {
                        int distance = cell.getInt(0);
                        int timeInSeconds = cell.getInt(1);
                        int timeInMinutes = timeInSeconds / 60; // 转换为分钟
                        distanceMatrix[i][j] = distance;
                        timeMatrix[i][j] = timeInMinutes;
                    } else {
                        // If there is only one value, assume it is the distance and the time is set to 0
                        int distance = cell.getInt(0);
                        distanceMatrix[i][j] = distance;
                        timeMatrix[i][j] = 0;
                    }
                }
            }
        }
    }

    // Parsing order data to create a list of Order objects
    List<Order> orders = new ArrayList<>();
    for (int i = 0; i < ordersData.length(); i++) {
        JSONObject orderJson = ordersData.getJSONObject(i);
        int orderId = i; // Use the order's index as the unique ID

        // Creating Collection Tasks
        Customer collectionCustomer = new Customer(
                orderJson.optString("CollectId", ""),
                "",
                orderJson.optInt("CollectSiteId", 0),
                0,
                orderJson.optString("EarliestCollect1", ""),
                orderJson.optString("LatestCollect1", ""),
                "",
                "",
                orderJson.optInt("CollectTimeInMinutes", 0),
                0,
                orderJson.optInt("Weight", 0),
                orderJson.optInt("Priority", 1), // Default priority is 1
                true,
                orderId
        );

        // Creating Delivery Tasks
        Customer deliveryCustomer = new Customer(
                "",
                orderJson.optString("DeliverId", ""),
                0,
                orderJson.optInt("DeliverSiteId", 0),
                "",
                "",
                orderJson.optString("EarliestDeliver1", ""),
                orderJson.optString("LatestDeliver1", ""),
                0,
                orderJson.optInt("DeliverTimeInMinutes", 0),
                orderJson.optInt("Weight", 0),
                orderJson.optInt("Priority", 1), // Default priority is 1
                false,
                orderId
        );

        // Only add orders with valid collection and delivery tasks
        if (!collectionCustomer.collectId.isEmpty() && !deliveryCustomer.deliverId.isEmpty()) {
            orders.add(new Order(orderId, collectionCustomer, deliveryCustomer));
        }
    }

    // Sorting Collection and Delivery Tasks in an Order by Priority and Earliest Time
    List<Customer> customers = new ArrayList<>();
    for (Order order : orders) {
        customers.add(order.collectionCustomer);
        customers.add(order.deliveryCustomer);
    }
    Collections.sort(customers); // sort

    // Parses vehicle data and creates a list of Vehicle objects.
    List<Vehicle> vehicles = new ArrayList<>();
    for (int i = 0; i < vehiclesData.length(); i++) {
        JSONObject vehicleData = vehiclesData.getJSONObject(i);
        Vehicle vehicle = new Vehicle(
                vehicleData.getString("Id"),
                vehicleData.optInt("StartSite", 0),
                vehicleData.optString("StartTime", "2022-01-01T08:00:00"), // Default start time
                vehicleData.optInt("EndSite", 0),
                vehicleData.getJSONArray("VehicleCapacity").getJSONObject(0).getInt("Weight")
        );
        vehicles.add(vehicle);
    }

    // Initialize the state of all vehicles
    List<VehicleState> vehicleStates = new ArrayList<>();
    for (Vehicle v : vehicles) {
        vehicleStates.add(new VehicleState(v)); // Create a vehicle state object for each vehicle and add it to the list
    }

    // Tracking of assigned and unassigned customers
    Set<Customer> assignedCustomers = new HashSet<>();
    List<Customer> unassignedCustomers = new ArrayList<>();

    // Uses a priority queue that sorts and assigns customers based on their priority and earliest available time
    PriorityQueue<Customer> customerQueue = new PriorityQueue<>(customers);

    int sequenceNo = 1; // Define the sequence number, starting with 1
    SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm"); // Format time to hours:minutes

    // Total consumption time in minutes
    int overallTotalConsumedTime = 0;

    // Start assigning customers
    while (!customerQueue.isEmpty()) {
        Customer customer = customerQueue.poll();
        if (assignedCustomers.contains(customer)) continue;

        // If it's a delivery mission, make sure at least one vehicle has completed the corresponding collection mission
        if (!customer.isCollection) {
            boolean hasCollected = false;
            for (VehicleState vs : vehicleStates) {
                if (vs.collectedOrderIds.contains(customer.orderId)) {
                    hasCollected = true;
                    break;
                }
            }
            if (!hasCollected) {
                // Unable to assign for now, try again later
                unassignedCustomers.add(customer);
                continue;
            }
        }

        // Find the most appropriate vehicle to assign to that customer
        VehicleState bestVehicle = null;
        int bestJourneyTime = 0;
        int bestDistance = 0;
        int bestWaitTime = 0;
        Date bestArrivalTime = null;

        // Iterate through all the vehicles to find the most appropriate one
        for (VehicleState vs : vehicleStates) {
            // If it is a delivery mission, make sure the vehicle has completed the corresponding collection mission
            if (!customer.isCollection && !vs.collectedOrderIds.contains(customer.orderId)) {
                continue; // This vehicle has not yet completed the corresponding collection task, skip the
            }

            // Calculate travel time and distance
            int targetLocation = customer.isCollection ? customer.collectSiteId : customer.deliverSiteId;
            int journeyTime = calculateJourneyTime(vs.currentLocation, targetLocation, timeMatrix);
            int distance = calculateDistance(vs.currentLocation, targetLocation, distanceMatrix);
            if (journeyTime == 0 && vs.currentLocation != targetLocation) continue; // Invalid route, skip

            // Calculate arrival time
            Date arrivalTime = new Date(vs.currentTime.getTime() + journeyTime * 60 * 1000);

            // Time Window for Customer Acquisition
            Date windowStart = customer.isCollection ? customer.earliestCollect1 : customer.earliestDeliver1;
            Date windowEnd = customer.isCollection ? customer.latestCollect1 : customer.latestDeliver1;

            if (windowStart == null || windowEnd == null) {
                // If no time window is specified, it is assumed to be feasible
                windowStart = vs.currentTime;
                windowEnd = new Date(vs.currentTime.getTime() + MAX_ROUTE_DURATION * 60 * 1000);
            }

            // Check if the arrival time is within the time window
            if (arrivalTime.after(windowEnd)) {
                continue; // Cannot be assigned to that vehicle within the time window
            }

            // If arrival time is earlier than the earliest time, you need to wait
            int waitTime = 0;
            if (arrivalTime.before(windowStart)) {
                waitTime = (int) ((windowStart.getTime() - arrivalTime.getTime()) / (60 * 1000));
                arrivalTime = windowStart;
            }

            // Calculation of end-of-service time
            Date departureTime = new Date(arrivalTime.getTime() + (customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes) * 60 * 1000);

            // Calculate new cumulative driving time and hours worked
            int newTotalDrivingTime = vs.totalDrivingTime + journeyTime;
            int serviceTime = customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes;
            int newTotalWorkingTime = vs.totalWorkingTime + journeyTime + waitTime + serviceTime;

            // Check if limits are exceeded
            if (newTotalDrivingTime > MAX_DRIVING_TIME || newTotalWorkingTime > MAX_ROUTE_DURATION) {
                continue; // Exceeds limits, cannot be distributed
            }

            // Choose the earliest arriving vehicle
            if (bestVehicle == null || arrivalTime.before(bestArrivalTime)) {
                bestVehicle = vs;
                bestArrivalTime = arrivalTime;
                bestJourneyTime = journeyTime;
                bestDistance = distance;
                bestWaitTime = waitTime;
            }
        }

        if (bestVehicle != null) {  // If the right vehicle is found
            // If this is the first time the vehicle has been assigned a task, add the task “start”.
            if (bestVehicle.assignments.isEmpty()) {
                Assignment startAssignment = new Assignment(
                        bestVehicle.vehicle.id,
                        "Vehicle " + bestVehicle.vehicle.id + " start",
                        "0h0m",
                        timeFormatter.format(bestVehicle.currentTime),
                        "0h0m",
                        "0h0m",
                        "0h0m",
                        formatDateTime(bestVehicle.currentTime),
                        "",
                        "",
                        "",
                        "",
                        0
                );
                bestVehicle.assignments.add(startAssignment);
            }

            int targetLocation = customer.isCollection ? customer.collectSiteId : customer.deliverSiteId;
            int journeyTime = calculateJourneyTime(bestVehicle.currentLocation, targetLocation, timeMatrix);
            int distance = calculateDistance(bestVehicle.currentLocation, targetLocation, distanceMatrix);
            Date arrivalTime = new Date(bestVehicle.currentTime.getTime() + journeyTime * 60 * 1000);

            // Time Window for Customer Acquisition
            Date windowStart = customer.isCollection ? customer.earliestCollect1 : customer.earliestDeliver1;
            Date windowEnd = customer.isCollection ? customer.latestCollect1 : customer.latestDeliver1;
            if (windowStart == null || windowEnd == null) {
                windowStart = bestVehicle.currentTime;
                windowEnd = new Date(bestVehicle.currentTime.getTime() + MAX_ROUTE_DURATION * 60 * 1000);
            }

            // If arrival time is earlier than the earliest time, you need to wait
            int waitTime = 0;
            if (arrivalTime.before(windowStart)) {
                waitTime = (int) ((windowStart.getTime() - arrivalTime.getTime()) / (60 * 1000));
                arrivalTime = windowStart;
            }

            // Calculation of end-of-service time
            Date departureTime = new Date(arrivalTime.getTime() + (customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes) * 60 * 1000);

            // Create and add task assignments
            Assignment assignment = new Assignment(
                    bestVehicle.vehicle.id,
                    customer.isCollection ? customer.collectId : customer.deliverId, // JobId
                    formatTime(journeyTime),                                    // JourneyTime
                    timeFormatter.format(arrivalTime),                         // ArrivalTime
                    formatTime(waitTime),                                      // WaitTime
                    "0h0m",                                                    // DelayTime
                    formatTime(customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes), // ServiceTime
                    formatDateTime(departureTime),                             // DepartureTime
                    "",                                                        // Break1Time
                    "",                                                        // Break1Duration
                    "",                                                        // Break2Time
                    "",                                                        // Break2Duration
                    distance                                                   // Distance
            );
            bestVehicle.assignments.add(assignment);

            // Update Vehicle Status
            bestVehicle.currentTime = departureTime;
            bestVehicle.currentLocation = targetLocation;
            bestVehicle.totalDrivingTime += journeyTime + (customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes);
            bestVehicle.totalWorkingTime += journeyTime + waitTime + (customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes);
            assignedCustomers.add(customer);
            overallTotalConsumedTime += journeyTime + waitTime + (customer.isCollection ? customer.collectTimeInMinutes : customer.deliverTimeInMinutes);

            // If it is a collection task, record the order ID that has been collected
            if (customer.isCollection) {
                bestVehicle.collectedOrderIds.add(customer.orderId);
            }

            // Check if you need to rest
            boolean needBreak = bestVehicle.totalDrivingTime >= MAX_CONTINUOUS_DRIVING;

            if (needBreak) {
                // If a break is needed, calculate the time window before the break
                Date afterBreak = new Date(bestVehicle.currentTime.getTime() + (MIN_BREAK1_DURATION + MIN_BREAK2_DURATION) * 60 * 1000);

                // Get the end time of the time window for the current task
                Date windowEndCurrent = customer.isCollection ? customer.latestCollect1 : customer.latestDeliver1;

                // If the time after the break would exceed the end of the customer's time window, the break is not inserted
                if (afterBreak.after(windowEndCurrent)) {
                    // No scheduled breaks to continue the mission
                    continue;
                }

                // Otherwise, arrange for rest
                String break1Time = timeFormatter.format(bestVehicle.currentTime);
                String break1Duration = formatTime(MIN_BREAK1_DURATION);
                String break2Time = timeFormatter.format(new Date(bestVehicle.currentTime.getTime() + MIN_BREAK1_DURATION * 60 * 1000 + MIN_BREAK2_DURATION * 60 * 1000));
                String break2Duration = formatTime(MIN_BREAK2_DURATION);

                // Get the latest Assignment object
                Assignment lastAssignment = bestVehicle.assignments.get(bestVehicle.assignments.size() - 1);
                lastAssignment.break1Time = break1Time;
                lastAssignment.break1Duration = break1Duration;
                lastAssignment.break2Time = break2Time;
                lastAssignment.break2Duration = break2Duration;

                // Update Vehicle Status
                bestVehicle.currentTime = afterBreak; // Update the current time of the vehicle
                bestVehicle.totalDrivingTime = 0; // Reset accumulated driving time
                bestVehicle.totalWorkingTime += MIN_BREAK1_DURATION + MIN_BREAK2_DURATION; // Accumulation of rest time to working time
                overallTotalConsumedTime += MIN_BREAK1_DURATION + MIN_BREAK2_DURATION; // Increase rest time to overall exertion time
            }
        } else {
            // If there are no vehicles available to assign to the customer, add to the unassigned list
            unassignedCustomers.add(customer);
        }
    }

    // All vehicles return to the finish line
    for (VehicleState vs : vehicleStates) {
        if (!vs.assignments.isEmpty()) { // Processing of utilized vehicles only
            // Calculate travel time and distance back to the end point
            int journeyToEndTime = calculateJourneyTime(vs.currentLocation, vs.vehicle.endSite, timeMatrix); // Calculation of travel time back to the end point
            int distanceToEnd = calculateDistance(vs.currentLocation, vs.vehicle.endSite, distanceMatrix); // Calculate the distance back to the end point

            Date arrivalAtEnd;
            if (journeyToEndTime > 0) {
                arrivalAtEnd = new Date(vs.currentTime.getTime() + journeyToEndTime * 60 * 1000); // Arrival time at return destination
            } else {
                arrivalAtEnd = vs.currentTime; // Already at the finish line
            }

            // Total time consumed for updating
            overallTotalConsumedTime += journeyToEndTime;

            // Create and add an “end” task
            Assignment endAssignment = new Assignment(
                    vs.vehicle.id,
                    "Vehicle " + vs.vehicle.id + " end",
                    formatTime(journeyToEndTime),
                    timeFormatter.format(arrivalAtEnd),
                    "0h0m",
                    "0h0m",
                    "0h0m",
                    formatDateTime(arrivalAtEnd),
                    "",
                    "",
                    "",
                    "",
                    distanceToEnd
            );
            vs.assignments.add(endAssignment);
        }
    }

    // Print header rows and output column names
    System.out.println("VehicleName,JobId,JourneyTime,ArrivalTime,WaitTime,DelayTime,ServiceTime,DepartureTime,Break1Time,Break1Duration,Break2Time,Break2Duration,Distance,SequenceNo");

    // Initialize the serial number
    sequenceNo = 1;

    // Prints all the tasks for each vehicle in the order in which they are ordered
    for (VehicleState vs : vehicleStates) {
        if (!vs.assignments.isEmpty()) {
            for (Assignment assignment : vs.assignments) {
                assignment.print(sequenceNo++);
            }
        }
    }

    // Handling of unassigned customers
    if (!unassignedCustomers.isEmpty()) {
        System.out.println("Unassigned Customers:"); // Printing information on unassigned customers
        for (Customer uc : unassignedCustomers) {
            if (uc.isCollection) {
                System.out.printf("Collection Task - Customer ID: %s Cannot be assigned to any vehicle within the time window.%n", uc.collectId);
            } else {
                System.out.printf("Delivery Task - Customer ID: %s Cannot be assigned to any vehicle within the time window or the collection task has not been assigned.%n", uc.deliverId);
            }
        }
    }

    // Total Printing Consumption Time
    //System.out.printf("Overall Total Consumed Time: %s%n", formatTime(overallTotalConsumedTime));
}
 
 

这里就是算法的核心部分了,这里把消耗的总时间注释了,需要显示时间的把注释符号删去即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值