2023-04-26:给定一个数组componets,长度为A,
componets[i] = j,代表i类型的任务需要耗时j
给定一个二维数组orders,长度为M,
orders[i][0]代表i号订单下单时间
orders[i][1]代表i号订单是哪种类型的任务,毫无疑问orders[i][1] < A
一开始所有流水线都在0时刻待命,
给定一个正数nums,表示流水线的数量,流水线编号为0 ~ nums-1
每一个流水线可以承接任何类型的任务,耗时就是componets数组给定的
所有订单的下单时间一定是有序的,也就是orders数组,是根据下单时间排序的
每一个订单开始执行的时间不能早于下单时间,
如果有多个流水线都可以执行当前订单,选择编号最小的流水线
根据上面说的任务执行细节,去依次完成所有订单
返回长度为M的数组ans,也就是和orders等长
ans[i][0]代表i号订单是由哪条流水线执行的
ans[i][1]代表i号订单的完成时间
1 <= A <= 10^5
1 <= M <= 10^5
1 <= nums <= 10^5
1 <= 时间数值 <= 10^5。

答案2022-04-26:

第一种算法大体过程:

  1. 初始化一个长度为 nums 的流水线数组 lines,初始值都为 0。

  2. 遍历订单数组 orders 中的每个订单 i,获取订单的下单时间 startTime 和任务类型 typ。

  3. 在流水线数组 lines 中查找第一个可用的流水线 usei,使得 lines[usei] <= startTime。如果找到了可用的流水线,将此订单分配给该流水线;否则,寻找结束时间最早的流水线 early,并将此订单分配给 early 流水线。

  4. 更新流水线数组 lines 中对应流水线的状态,即 lines[usei] = ans[i][1],其中 ans[i][1] 是该订单完成的时间。

  5. 将当前订单的结果保存到输出数组 ans 中,即 ans[i][0] = usei,ans[i][1] = lines[usei]。

  6. 返回 ans 数组。

时间复杂度为 O(nums * M),其中 nums 是流水线数量,M 是订单数量。空间复杂度为 O(M)。

第二种算法大体过程:

  1. 初始化一个长度为 nums 的可用流水线堆 canUseLines,以及一个睡眠流水线堆 sleepLines。

  2. 遍历订单数组 orders 中的每个订单 i,获取订单的下单时间 startTime 和任务类型 jobType。

  3. 从睡眠流水线堆 sleepLines 中弹出所有满足条件的流水线,并将这些流水线加入可用流水线堆 canUseLines 中。

  4. 从可用流水线堆 canUseLines 中选择编号最小的流水线 use,并将此订单分配给该流水线。

  5. 更新流水线的状态,即将 use 流水线从可用流水线堆 canUseLines 中弹出,更新其结束时间为此订单完成时间 ans[i][1],然后将其加入到睡眠流水线堆 sleepLines 中。

  6. 将当前订单的结果保存到输出数组 ans 中,即 ans[i][0] = use.index,ans[i][1] = use.time。

  7. 返回 ans 数组。

时间复杂度为 O(M * log(nums)),其中 M 是订单数量,nums 是流水线数量。由于使用了堆来维护可用流水线和睡眠流水线,每次操作的时间复杂度是 log(nums),因此总时间复杂度为 M * log(nums)。空间复杂度为 O(nums + M),即可用流水线堆和睡眠流水线堆的大小之和,加上输出数组 ans 的大小。

go完整代码如下:

package main

import (
"container/heap"
"fmt"
"math"
"math/rand"
"sort"
"time"
) func Times1(nums int, jobTimes []int, orders [][]int) [][]int {
lines := make([]int, nums)
n := len(orders)
ans := make([][]int, n)
for i := 0; i < n; i++ {
ans[i] = make([]int, 2)
}
for i := 0; i < n; i++ {
start, typ := orders[i][0], orders[i][1]
usei := -1
for j := 0; j < nums; j++ {
if lines[j] <= start {
usei = j
break
}
}
if usei != -1 {
ans[i][0] = usei
ans[i][1] = start + jobTimes[typ]
} else {
early := math.MaxInt32
for j := 0; j < nums; j++ {
if lines[j] < early {
early = lines[j]
usei = j
}
}
ans[i][0] = usei
ans[i][1] = early + jobTimes[typ]
}
lines[usei] = ans[i][1]
}
return ans
} type Line struct {
time int
index int
} type WakeUpHeap []*Line func (h WakeUpHeap) Len() int { return len(h) }
func (h WakeUpHeap) Less(i, j int) bool {
if h[i].time != h[j].time {
return h[i].time < h[j].time
}
return h[i].index < h[j].index
}
func (h WakeUpHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *WakeUpHeap) Push(x interface{}) {
*h = append(*h, x.(*Line))
}
func (h *WakeUpHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
} type IndexHeap []*Line func (h IndexHeap) Len() int { return len(h) }
func (h IndexHeap) Less(i, j int) bool { return h[i].index < h[j].index }
func (h IndexHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IndexHeap) Push(x interface{}) {
*h = append(*h, x.(*Line))
}
func (h *IndexHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
} func Times2(nums int, componets []int, orders [][]int) [][]int {
n := len(orders)
// 睡眠堆
sleepLines := make(WakeUpHeap, 0)
heap.Init(&sleepLines)
// 可用堆
canUseLines := make(IndexHeap, nums)
for i := 0; i < nums; i++ {
canUseLines[i] = &Line{0, i}
}
heap.Init(&canUseLines)
ans := make([][]int, n)
for i := 0; i < n; i++ {
ans[i] = make([]int, 2)
}
for i := 0; i < n; i++ {
startTime, jobType := orders[i][0], orders[i][1]
// 当前订单在start时刻下单,所有唤醒时间比time早的流水线全可以考虑
for len(sleepLines) > 0 && sleepLines[0].time <= startTime {
heap.Push(&canUseLines, heap.Pop(&sleepLines))
}
// 如果可以使用的流水线不存在
// 比如,2条流水线
// 第0个订单,1时刻开始,用时100万,流水线A在100万+1时刻醒来
// 第1个订单,2时刻开始,用时100万,流水线B在100万+2时刻醒来
// 轮到第3个订单,3时刻开始,用时100万
// 会发现可用流水线已经没有了,此时需要等到流水线A在100万+1时刻醒来,做当前订单
var use *Line
if len(canUseLines) == 0 {
// 如果当前时刻,可以使用的流水线不存在,需要等到可以唤醒的最早那个
// 如果可以唤醒的最早流水线,不只一个
// 选编号小的,看比较器的注释
use = heap.Pop(&sleepLines).(*Line)
ans[i][1] = use.time + componets[jobType]
} else {
// 如果当前时刻,可以使用的流水线存在,需要使用编号最小的
use = heap.Pop(&canUseLines).(*Line)
ans[i][1] = startTime + componets[jobType]
}
ans[i][0] = use.index
use.time = ans[i][1]
heap.Push(&sleepLines, use)
}
return ans
} func randomArray(n, v int) []int {
ans := make([]int, n)
rand.Seed(time.Now().UnixNano())
for i := 0; i < n; i++ {
ans[i] = rand.Intn(v) + 1
}
return ans
} func randomMatrix(n, a, b int) [][]int {
ans := make([][]int, n)
rand.Seed(time.Now().UnixNano())
for i := 0; i < n; i++ {
ans[i] = []int{rand.Intn(a) + 1, rand.Intn(b)}
}
return ans
} func main() {
N := 100
M := 300
V := 10000
testTimes := 5000
fmt.Println("功能测试开始")
for i := 0; i < testTimes; i++ {
nums := rand.Intn(N) + 1
orderNumber := rand.Intn(M) + 1
types := rand.Intn(N) + 1
componets := randomArray(types, V)
orders := randomMatrix(orderNumber, V, types)
sort.Slice(orders, func(i, j int) bool { return orders[i][0] < orders[j][0] })
ans1 := Times1(nums, componets, orders)
ans2 := Times2(nums, componets, orders)
for j := range ans1 {
if ans1[j][0] != ans2[j][0] || ans1[j][1] != ans2[j][1] {
fmt.Println("出错了!")
fmt.Println(nums)
for _, num := range componets {
fmt.Printf("%d ", num)
}
fmt.Println()
for _, order := range orders {
fmt.Printf("(%d, %d) ", order[0], order[1])
}
fmt.Println()
fmt.Print("ans1 : ")
for _, cur := range ans1 {
fmt.Printf("(%d, %d) ", cur[0], cur[1])
}
fmt.Println()
fmt.Print("ans2 : ")
for _, cur := range ans2 {
fmt.Printf("(%d, %d) ", cur[0], cur[1])
}
fmt.Println()
return
}
}
}
fmt.Println("功能测试结束") fmt.Println("性能测试开始")
N = 100000
M = 500000
V = 100000
nums := N
componets := randomArray(N, V)
orders := randomMatrix(M, V, N)
sort.Slice(orders, func(i, j int) bool { return orders[i][0] < orders[j][0] })
fmt.Println("流水线数量 : ", N)
fmt.Println("订单数量 : ", M)
fmt.Println("时间数值范围 : ", V)
start := time.Now()
Times2(nums, componets, orders)
end := time.Now()
fmt.Println("运行时间 : ", end.Sub(start).Milliseconds(), " 毫秒")
fmt.Println("性能测试结束")
}

2023-04-26:给定一个数组componets,长度为A, componets[i] = j,代表i类型的任务需要耗时j 给定一个二维数组orders,长度为M, orders[i][0]代表i的更多相关文章

  1. JS数组 二维数组 二维数组的表示 方法一: myarray[ ][ ];方法二:var Myarr = [[0 , 1 , 2 ],[1 , 2 , 3, ]]

    二维数组 一维数组,我们看成一组盒子,每个盒子只能放一个内容. 一维数组的表示: myarray[ ] 二维数组,我们看成一组盒子,不过每个盒子里还可以放多个盒子. 二维数组的表示: myarray[ ...

  2. 《java入门第一季》二维数组三个案例详解

    案例一:遍历二维数组 /* 需求:二维数组遍历 外循环控制的是二维数组的长度,其实就是一维数组的个数行数. 内循环控制的是一维数组的长度,每一行,一维数组元素分别的个数. */ class Array ...

  3. C/C++二维数组的用法

    二维数组在存储时按行优先连续存储,数组名是一个二维指针,如 int a[3][2] 中,a 是一个二维指针,而a[0],a[1],a[2]都相当于普通的一位数组的数组名,是一个固定值的指针. 二维数组 ...

  4. php对二维数组进行相关操作(排序、转换、去空白等)

    php对二维数组进行相关操作(排序.转换.去空白等) 投稿:lijiao 字体:[增加 减小] 类型:转载 时间:2015-11-04   这篇文章主要介绍了php对二维数组进行相关操作,包括php对 ...

  5. [zt]C++二维数组讲解、二维数组的声明和初始化

    定义: int *pia = new int[10]; // array of 10 uninitialized ints 此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向 ...

  6. Java数组之二维数组

    Java中除了一维数组外,还有二维数组,三维数组等多维数组.本文以介绍二维数组来了解多维数组. 1.二维数组的基础 二维数组的定义:二维数组就是数组的数组,数组里的元素也是数组. 二维数组表示行列二维 ...

  7. Java编程基础阶段笔记 day06 二维数组

    二维数组 笔记Notes 二维数组 二维数组声明 二维数组静态初始化与二位初始化 二维数组元素赋值与获取 二维数组遍历 二维数组内存解析 打印杨辉三角 Arrays工具类 数组中常见的异常 二维数组 ...

  8. (java)剑指offer二维数组中的查找

    在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从 上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. pu ...

  9. 问题 A: 【动态规划】采药_二维数组_一维数组

    问题 A: [动态规划]采药 时间限制: 1 Sec  内存限制: 64 MB提交: 35  解决: 15[提交][状态][讨论版] 题目描述 山洞里有一些不同的草药,采每一株都需要一些时间,每一株也 ...

  10. C++使用VARIANT实现二维数组的操作

    C++使用VARIANT实现二维数组的操作 VARIANT变量是COM组件之间互相通信的重要的参数变量之一,它可以容纳多种不同的类型,如short.long.double等,包括各类指针和数组.组件之 ...

随机推荐

  1. 【Beat】Scrum Meeting 4

    时间:2021年6月29日 1.各个成员今日完成的任务以及贡献小时数 姓名 今日完成任务 贡献小时数 鑫 继续进行bug的修改 4 荣娟 继续进行bug的修改 4 亚楠 继续进行bug的修改 4 桂婷 ...

  2. NTP同步时间

    什么是NTPNTP:Network Time Protocol(网络时间协议) ️ NTP 是用于同步网络中计算机时间的协议.它的用途是把计算机的时钟同步到世界协调时UTC. UTC:Universa ...

  3. mysql生成随机日期

    生成一天内随机时间 select sec_to_time(rand() * 86400); 生成一天内随机时间,floor取整秒 select sec_to_time(floor(rand() * 8 ...

  4. java的数据和表达式

    一.基本语法元素 1.空白和注释及语句 (1)空白: 换行符.回车符.空格键.水平定位键(Tab) 编译器会忽略掉多余的空白 作用:增加程序的易读性 (2)注释:主要作用是将代码解释其功能和作用,在编 ...

  5. Python3程序捕获Ctrl+C终止信号

    技术背景 对于一些连续运行或者长时间运行的Python程序而言,如服务器的后端,或者是长时间运行的科学计算程序.当我们涉及到一些中途退出的操作时,比如使用Ctrl+C来退出正在运行的程序.这种场景的出 ...

  6. Python学习笔记--函数来啦!

    函数 函数,就是组织好的,可重复使用的,用来实现特定功能的代码块 实际的小案例:不使用内置函数len,利用函数知识计算出字符串的长度 实现: 函数的基础定义语法 案例:自动查核酸 实现: 函数的传入参 ...

  7. Linux & 标准C语言学习 <DAY9_1>

        2.函数传参:         1.函数中定义的变量属于该函数,出了该函数就不能再被别的函数直接使用         2.实参与形参之间是以赋值的方式进行传递数据的,并且是单项值传递     ...

  8. 解放AI生产力——ComfyUI

    最近状态不好,所以这几天基本没干什么,就分享一下和AI绘画工作流有关的东西吧. 此前我都没有抱着一种教学的心态来写博客,因为我所掌握的东西实在太过简单,只要一说大家就会了,我害怕我在人群里失去自己的特 ...

  9. 从零开始学Java系列之Java是什么?它到底是个啥?

    全文大约[5000]字,不说废话,只讲可以让你学到技术.明白原理的纯干货!文章带有丰富案例及配图,只为让你更好的理解和运用文中的技术概念,给你带来具有足够的思想启迪...... ----------- ...

  10. P6666 [清华集训2016] 数据交互 题解

    ## P6666 [清华集训2016] 数据交互 题解 ### 简要题意: n个点的树,m次操作,分别为添加一条路径$(u_i,v_i,w_i)$,和撤消一条路径,每一次操作后求出一条路径使得与这条路 ...