过桥的时间(leetcode -- 2532)

目录

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;
	}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值