有n个餐厅和m个外卖员,每个餐厅在某个时间点会产生一个外卖订单,这些订单都有产生时间,所需送达时间和优先级。外卖员在空闲的时候会选择最优的订单来配送,直到所有订单都被送达。
这是规则:
1.每个餐厅的订单,优先级高的订单优先,其次是所需送达时间最短的订单,再次是产生时间最早的订单。
2.外卖员在空闲的时候会从所有餐厅的最高优先级选一个所需送达时间最短的订单配送,如果所需送达时间相同则选择餐厅编号最小的。输入:第一行输入三个n m p,p表示订单数,随后有p行,每行4个数字 分别是餐厅编号,产生时间,优先级和所需送达时间。
输出:p行,每行对应表示每个订单被送达的时间点。
给定的订单信息:
● Order 1: 餐厅编号 1,产生时间 1,优先级 2,送达时间 5
● Order 2: 餐厅编号 1,产生时间 4,优先级 3,送达时间 2
● Order 3: 餐厅编号 2,产生时间 2,优先级 1,送达时间 4
● Order 4: 餐厅编号 2,产生时间 5,优先级 2,送达时间 1
问题分析
第一个单位的时间:餐厅1出餐了,order1做好了,这个时候队列里只有order1,假设是外卖小哥1去送餐。
在第2个时间到第六个单位的时间里外卖小哥1都在送餐。
第2个时间里,餐厅2已经做好了order3,外卖小哥2空闲,所以这单他去送,时间是2+4=6
前两单的甘特图如下:
我们可以看出来,只有在第六个时间里,外卖小哥1和外卖小哥2才能把餐送完。
在第四个时间和第五个时间,order2和order4都做完了,但是由于order2的优先级更高,所以order2会排在order4的前面。
等到时间6完了,两个外卖哥都空闲了,外卖哥1送order2,外卖哥2送order4。
总体的甘特图如下:
代码实现
我们希望优先级队列可以被外卖员,也就是我们的消费者监听。可以把外卖员定义成两个能够并发执行的线程,当我们往队列里放订单的时候,就去通知他可以去执行了。
这里最难的还是代码实现,我们要知道,这个餐一被放到队列里,外卖员就要去送了,他去送餐的时间里,只有另一个外卖员空闲。其实外卖员就像两个并发执行的线程。我们这里只能通过唤醒和睡眠来模拟外卖员的行动了,当队列里有餐,就唤醒外卖员去送餐,他送餐的过程就休眠线程。
1.定义一个优先级队列,根据题目的要求进行订单的存放。
private static final PriorityQueue<Order> orderQueue = new PriorityQueue<>(new Comparator<Order>() {
@Override
public int compare(Order o1, Order o2) {
if (o1.priority != o2.priority) {
return o2.priority - o1.priority; // 优先级高的优先
} else if (o1.deliveryTime != o2.deliveryTime) {
return o1.deliveryTime - o2.deliveryTime; // 所需送达时间短的优先
} else if (o1.createTime != o2.createTime) {
return o1.createTime - o2.createTime; // 产生时间早的优先
} else {
return o1.res_id - o2.res_id; // 餐厅编号小的优先
}
}
});
2.读取所有的订单,让它们根据产生时间进行排序
List<Order> orderList = new ArrayList<>();
// 读订单并加入队列
for (int i = 0; i < p; i++) {
int res_id = sc.nextInt();
int createTime = sc.nextInt();
int priority = sc.nextInt();
int deliveryTime = sc.nextInt();
Order order = new Order(i, res_id, createTime, priority, deliveryTime);
//将订单加入到订单队列中
orderList.add(order);
}
//订单列表中的订单按照createTime排序
orderList.sort(Comparator.comparingInt(order -> order.createTime));
3.模拟时间的消耗过程,用一个变量表示当前的时间流逝,当产生时间==当前时间的时候,就把订单放到优先级队列中
int currentTime = 0;//当前时间
int index = 0;
//模拟时间的推移
while (index < orderList.size()) {
lock.lock();
try {
// 只在订单的 createTime 等于当前时间时,将订单放入队列
while (index < orderList.size() && orderList.get(index).createTime == currentTime) {
orderQueue.offer(orderList.get(index));
notEmpty.signal(); // 唤醒等待的线程
index++;
}
} finally {
lock.unlock();
}
Thread.sleep(1000L); // 模拟时间的流逝
currentTime++;
}
4.当优先级队里有订单的时候,唤醒外卖小哥线程去执行。
int[] deliveryTimes = new int[p];
// 启动外卖员线程
for (int i = 0; i < m; i++) {
new Thread(() -> {
int currentTime = 0; // 每个外卖员都有自己的当前时间
while (true) {
Order order;
lock.lock();
try {
while (orderQueue.isEmpty()) {
try {
notEmpty.await(); // 等待有新订单进入队列
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
order = orderQueue.poll();
} finally {
lock.unlock();
}
try {
// 更新外卖员的当前时间,确保时间是累积的
currentTime = Math.max(currentTime, order.createTime) + order.deliveryTime;
deliveryTimes[order.id] = currentTime;
System.out.println("Order " + order.id + " delivered at time " + currentTime);
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}).start();
}
代码
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
// 声明为静态变量,使得所有外卖员线程共享
private static final PriorityQueue<Order> orderQueue = new PriorityQueue<>(new Comparator<Order>() {
@Override
public int compare(Order o1, Order o2) {
if (o1.priority != o2.priority) {
return o2.priority - o1.priority; // 优先级高的优先
} else if (o1.deliveryTime != o2.deliveryTime) {
return o1.deliveryTime - o2.deliveryTime; // 所需送达时间短的优先
} else if (o1.createTime != o2.createTime) {
return o1.createTime - o2.createTime; // 产生时间早的优先
} else {
return o1.res_id - o2.res_id; // 餐厅编号小的优先
}
}
});
// ReentrantLock 和 Condition 用于同步和条件等待
private static final Lock lock = new ReentrantLock();
private static final Condition notEmpty = lock.newCondition();
static class Order {
int id; // 订单ID(输入顺序)
int res_id; // 餐厅编号
int createTime; // 创建时间
int priority; // 优先级
int deliveryTime; // 送达时间
public Order(int id, int res_id, int createTime, int priority, int deliveryTime) {
this.id = id;
this.res_id = res_id;
this.createTime = createTime;
this.priority = priority;
this.deliveryTime = deliveryTime;
}
}
public static void main(String[] args) throws InterruptedException {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 餐厅数量
int m = sc.nextInt(); // 外卖员数量
int p = sc.nextInt(); // 订单数量
List<Order> orderList = new ArrayList<>();
// 读订单并加入队列
for (int i = 0; i < p; i++) {
int res_id = sc.nextInt();
int createTime = sc.nextInt();
int priority = sc.nextInt();
int deliveryTime = sc.nextInt();
Order order = new Order(i, res_id, createTime, priority, deliveryTime);
//将订单加入到订单队列中
orderList.add(order);
}
//订单列表中的订单按照createTime排序
orderList.sort(Comparator.comparingInt(order -> order.createTime));
int[] deliveryTimes = new int[p];
// 启动外卖员线程
for (int i = 0; i < m; i++) {
new Thread(() -> {
int currentTime = 0; // 每个外卖员都有自己的当前时间
while (true) {
Order order;
lock.lock();
try {
while (orderQueue.isEmpty()) {
try {
notEmpty.await(); // 等待有新订单进入队列
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
order = orderQueue.poll();
} finally {
lock.unlock();
}
try {
// 更新外卖员的当前时间,确保时间是累积的
currentTime = Math.max(currentTime, order.createTime) + order.deliveryTime;
deliveryTimes[order.id] = currentTime;
System.out.println("Order " + order.id + " delivered at time " + currentTime);
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}).start();
}
int currentTime = 0;//当前时间
int index = 0;
//模拟时间的推移
while (index < orderList.size()) {
lock.lock();
try {
// 只在订单的 createTime 等于当前时间时,将订单放入队列
while (index < orderList.size() && orderList.get(index).createTime == currentTime) {
orderQueue.offer(orderList.get(index));
notEmpty.signal(); // 唤醒等待的线程
index++;
}
} finally {
lock.unlock();
}
Thread.sleep(1000L); // 模拟时间的流逝
currentTime++;
}
}
}