想象一下,你正在洗车工作,你的任务是管理应该先洗车的顺序。每辆车都有两个号码:
到达值描述了汽车到达队列的时间(以分钟为单位),洗车时间仅需5分钟,同时只能洗车1辆。
您不会按照队列中的顺序进行操作,而是始终选择优先级最低的队列中的汽车。如果洗衣队不忙,您将选择优先级最低的汽车。一旦洗涤团队准备就绪,您就会选择优先级最低的下一辆汽车。
现在我们想要编写一个程序来获取所有汽车的列表并打印汽车应该被选中的订单(汽车优先级值)。
输入将如下所示:
c
{ (a,b) , (a,b) , (a,b) }
a
是优先级值,b
是到货值,c
是汽车数量。列表将始终按值b的升序排序,第一个b
将始终为0.
例如,如果输入如下所示:
4
{ (5,0) , (3,0) , (2,3) , (1,4) }
输出应如下所示:
3
1
2
5
我目前所做的是使用存储桶排序(1到100)对优先级值进行排序,然后遍历所有存储桶并每次选择满足到达条件(arrival <= time)
的第一辆车,直到所有存储桶为空。每次我选车时,我也会将变量time
增加5,以跟踪下一回合可以选择的车辆。我还会跟踪到达的最低到达值,如果找不到汽车,则将time
分配给该值,这样如果到达时间的差异大于5,则循环不会卡住。
如果我们调用汽车数量C
并且存储桶数量为100(因为索引1到100),则在最坏的情况下,上述解决方案在O(C + 100C)
中运行。如果我在C ++中运行它并将C
设置为20000,则会导致大约10-15ms的运行时间。
所以,我的问题是,是否可以进一步优化这一点,以便我们获得具有更好时间复杂度的解决方案,例如O(C)
或至少O(50C)
?目标是获得0-4ms的运行时间。
优先级队列:
不幸的是,这种实施仍然需要10-15ms来处理20000辆汽车。
#include <stdio.h>
#include <vector>
#include <queue>
#include <functional>
using namespace std;
int main()
{
unsigned int carCount;
scanf("%d", &carCount);
priority_queue<unsigned int, vector<unsigned int>, greater<unsigned int>> q;
unsigned int readCount = 0;
unsigned int printCount = 0;
unsigned int priority = 0;
unsigned int arrival = 0;
unsigned int time = 0;
scanf("%d %d", &priority, &arrival);
while (printCount != carCount)
{
while (readCount != carCount)
{
if (arrival <= time)
{
q.push(priority);
++readCount;
if (readCount != carCount) scanf("%d %d", &priority, &arrival);
continue;
}
break;
}
if (q.empty())
{
time = time < arrival ? arrival : time;
continue;
}
printf("%d ", q.top());
q.pop();
++printCount;
time += 5;
}
printf("\n");
return 0;
}
利用已经分类的汽车:
不幸的是,这种实施仍然需要10-15ms来处理20000辆汽车。
#include <stdio.h>
#include <string.h>
using namespace std;
int main()
{
unsigned int carCount;
scanf("%d", &carCount);
unsigned int buckets[101];
memset(buckets, 0, sizeof(unsigned int) * 101);
unsigned int readCount = 0;
unsigned int printCount = 0;
unsigned int priority = 0;
unsigned int arrival = 0;
unsigned int time = 0;
scanf("%d %d", &priority, &arrival);
while (printCount != carCount)
{
while (readCount != carCount)
{
if (arrival <= time)
{
++buckets[priority];
++readCount;
if (readCount != carCount) scanf("%d %d", &priority, &arrival);
continue;
}
break;
}
unsigned int i;
for (i = 1; i != 101; ++i)
{
if (buckets[i] != 0)
{
printf("%d ", i);
--buckets[i];
++printCount;
time += 5;
break;
}
}
if (i == 101)
{
time = time < arrival ? arrival : time;
}
}
printf("\n");
return 0;
}
答案 0 :(得分:0)
看起来您可能没有充分利用输入数据的重要声明特征,即汽车已经按到达时间排序。
这使您可以随时执行铲斗排序。
分配您的100个桶和您的时间变量,就像您已经拥有的那样。但是,铲斗不是对所有车辆进行铲斗分类,而是仅对已到达的车辆进行分类。由于汽车已按到货时间排序,因此效率很高。只需检查阵列中的第一辆车是否已到达(通过与您当前的时间值比较),如果是,则将其扔到适当的优先级桶中(并从阵列中“移除”它)。重复,直到阵列中的第一辆车尚未到达。此时,您的水桶仅包含已到达的车辆。因此,从最低点开始迭代您的优先级桶,抓住您找到的第一辆车,将其输出,然后将其从桶中移除。增加你的时间价值。重复,直到你的汽车用完为止。
关于从列表中“移除”汽车的说明......没有必要通过实际从阵列中移除汽车来改变内存。相反,您可以简单地保留一个汽车计数器变量,该变量始终指向阵列中第一个未处理的汽车。由于输入数据永远不会被销毁或移动,因此这也允许您的存储桶只包含索引(或指针,如果您愿意)到输入数组,而不是汽车本身的副本。
通过再见,另一种方法是完全跳过桶,并用已到达的汽车的动态列表(或矢量)替换它们。当每辆车到达时,您需要优先在列表中找到它的位置。
无论哪种方式,主要的想法是:在给定的时间点,只关注已经到达的汽车。