目录
1.题目描述
2.如何思考
3.代码实现
题目描述:
如何思考:
我们先来分析一下难点。
这道题有一个很显著的难点就是状态的依赖性极其复杂。从示例我们也可以看出。例如,在过桥时各个状态发生了牵扯,分别有以下几种状态:
1.未过桥的要等待正在过桥的人过桥;
2.没有人过桥,有一岸没有等待人员,那么有等待人员的岸边要派遣效率最低的人过桥
3.没有人过桥,但两岸都人有等待。
4.随着时间线推移,等待人员可能变动,人员变动意味着派遣位次可能发生变化。
而在不过桥时,每个人的行为是独立的!也就是说,他们的作业是并行的。
面对以上的复杂状态,我们采取策略1--分块处理。
策略1:分块处理
分块处理,就是分状态来处理。过桥时会状态纠缠,而不过桥时不会。那么我们就分开处理这两种情况。
而在状态纠缠时,我们会遇到一些题目未做出规定的状态!但计算机的运行是离散化的,单一处理的。所以我们需要对一些状态采取策略2--做出规定,单一化
策略2:做出规定,单一化
例如在过桥时,我们会发生3的情况,这时候我们需要规定如果两岸都有等待人员,那一岸的人员先行过桥。这里我们做出规定:右岸人员先过桥。其次,不过桥时状态时并行的,但是我们的计算机处理是单一的。所以我们要做出改变。单一化并行状态有以下3个方案:
方案1:统计各个工人的时间进度条,以及所处的状态(在左岸/右岸工作)。
方案2:记录每次任务的完成时间,如果在当前的时间线前,那么则判定完成。
方案3:统计每一个时间进度下,每一个员工的执行状态 -- 方案1 + 2变体
我们很快能废除方案3,因为它的存储代价是庞大的。
单一化的方案构思角度是简单的。从事务角度,每个 事务的进程都单一化,相互独立互不干扰;
从时间线角度,我们依旧推进这单一的时间,只不过将事物单一化成“时间线”并标记在总时间线上。
比较两个方案,方案1稍显麻烦,因为他需要和其他进程比较,每次更动时间早的,以推进时间线。而方案2,则自带比较功能,只要我们记录当前的推进进度在何处即可。所以我们采用方案2
在主架构上我们大致能处理,但我们仍有难点,如何维护过桥时的状态?
因为这里过桥的状态是“排队等待”,并且加了一点“VIP通道”的意味,如果你对这些实际功能的实现有所了解,那么你很快就能反应过来这里需要优先队列。
如果你不了解,那么根据题目的意思,我们需要按照题目的规则进行一定的有序化!那么有序化,我们可以用排序算法,用数据结构--堆。因为我们每次只要派遣一个过桥,堆显然更为合理。排序算法也可以,在之后的算法中就需要记录排序后的员工编号。
代码实现:
class solution {
private:
using PII = pair<int, int>;
public:
//k位工人,n个箱子,time各个事项时间
int findCrossingTime(int n, int k, vector<vector<int> >& time) {
//{效率, 编号},也可以使用functional来完成,编写一个比较程序
priority_queue<PII> wait_left, wait_right;
//{工作完成时间, 编号}
priority_queue<PII, vector<PII>, greater<PII> > work_left, work_right;
//所有员工压入左岸等待队列,并维护派遣次序
for (int i = 0; i < k; ++i)
{
wait_left.push({time[i][0] + time[i][2], i});
}
int cur_time = 0;//记录当前时间线(当前进程推进程度)
while (n || !wait_right.empty() || !work_right.empty())
{
//处理过桥时
if (!wait_right.empty())//有人等待回左岸
{
int id = wait_right.top().second;
wait_right.pop();
cur_time += time[id][2];
work_left.push({cur_time + time[id][3], id});
}
else if (n && !wait_left.empty())//货物没有取完并且左岸有人等待则排遣人选取货物
{
int id = wait_left.top().second;
wait_left.pop();
cur_time += time[id][0];
work_right.push({cur_time + time[id][1], id});
--n;//未取货物-1
}
else//执行人员都处于工作状态,时间过渡到有人完成任务;
{
if (!work_left.empty() && work_right.empty())//仅左岸有工作人员
{
cur_time = work_left.top().first;
}
else if (work_left.empty() && !work_right.empty())//仅右岸有
{
cur_time = work_right.top().first;
}
else//两岸都有
{
cur_time = min(work_left.top().first, work_right.top().first);
}
}
//当前时间线,左边工作者可以完成任务的加入等待队列
while (!work_left.empty() && work_left.top().first <= cur_time)
{
int id = work_left.top().second;
work_left.pop();
wait_left.push({time[id][0] + time[id][2], id});
}
//当前时间线,右边工作者可以完成任务的加入等待队列
while (!work_right.empty() && work_right.top().first <= cur_time)
{
int id = work_right.top().second;
work_right.pop();
wait_right.push({ time[id][0] + time[id][2], id });
}
}
return cur_time;
}
};