Algorithm 此问题的线性时间解
我在一次面试中被问到这个问题 你站在0,你必须到达一个位置X。你可以跳到D(1到D)。如果X>D,很明显,你无法在初始跳跃时到达位置X 现在,从1到N,每秒钟都有随机位置出现的分片。这是作为零索引数组a[k]给出的,其中a[k]表示在第k秒出现的分片的位置。你必须找出,在哪一秒你可以到达(或穿过)目的地X 如果可以在初始值或[0]之后返回0,或返回最小秒数。如果在所有平铺之后仍然不可能,那么返回-1 限制条件:Algorithm 此问题的线性时间解,algorithm,language-agnostic,Algorithm,Language Agnostic,我在一次面试中被问到这个问题 你站在0,你必须到达一个位置X。你可以跳到D(1到D)。如果X>D,很明显,你无法在初始跳跃时到达位置X 现在,从1到N,每秒钟都有随机位置出现的分片。这是作为零索引数组a[k]给出的,其中a[k]表示在第k秒出现的分片的位置。你必须找出,在哪一秒你可以到达(或穿过)目的地X 如果可以在初始值或[0]之后返回0,或返回最小秒数。如果在所有平铺之后仍然不可能,那么返回-1 限制条件: 1你说的线性是指瓷砖数量的线性,对吗 如果是这样,这个解决方案(在Java中)只迭代
1你说的线性是指瓷砖数量的线性,对吗 如果是这样,这个解决方案(在Java中)只迭代一次tile数组 在每次迭代中,它还需要迭代D和X次,但它与平铺数组的大小成线性关系 如果听起来和你要找的相似,请告诉我 注意:为了简化,我假设位置“0”处的磁贴在第二个数字“0”处可用,因此有效地将第二个“0”视为仅存在您所站立的磁贴的时间,然后其他磁贴在第1、2秒等处出现
公共类TestTiles{
公共静态int-calculateSecond(整数x、整数d、int[]tiles){
//首先使所有位置都不可到达(false)
boolean[]posReachable=新的boolean[x+1];
//每秒钟只迭代一次
for(int second=0;second 对于(int pos=tile;(pos以下建议应花费时间O(N*log(min(N,X/D))。请特别注意,这是在O(N*log(N))中,因此比您提出的算法或mcdowella提出的优先级队列算法有更好的界限;在O(N*(X+D))中,因此比eugenioy提出的算法有一个更好的界;不随D的增加而增加(正如mcdowella的数组算法、eugenioy的算法和coproc的算法所做的);而且对于固定的X,它在O(N)中
我们的想法是保留一组我们仍然需要找到路径的区间。我们将把这组区间存储在一个平衡树中,其键是区间的下界,其值是上界。当我们看到一个新的磁贴时,我们将找到包含此磁贴的区间(如果有的话),并在磁贴周围拆分该区间,扔掉任何值结果间隔小于D。当我们的地图为空时,我们就完成了
下面是Haskell中的完整实现
导入数据.Ix
导入数据。映射
导入符合条件的数据。映射为M
--设置:初始间隔集仅为0到x之间的单例
搜索::Int->Int->[Int]->可能是Int
search d x=search_d(insertCandidate d 0 x空)
搜索:Int->Map Int Int->[Int]->可能是Int
搜索d=去哪里
--第一个基本情况:我们找到了所有我们关心的路径
go间隔124; M.null间隔=仅为0
--第二种基本情况:我们的瓷砖用完了,但仍然没有所有的路径
什么也不做
--最后一个例子:我们需要采取一个时间步。添加一个,然后递归搜索剩余的时间步
转到间隔(平铺:平铺)=(1+)转到新间隔平铺,其中
newIntervals=案例lookupLE平铺间隔
只是(低,高)|在范围内(低,高)瓷砖
->插入Candidate d lo瓦
.insertCandidate d瓷砖hi
.删除lo
$interval
_->间隔
--只保留非平凡的间隔
insertCandidate::Int->Int->Int->Map Int Int->Map Int Int
插入日期d lo hi m
|hi-lo我会一个接一个地处理阵列中的磁贴,跟踪最大可达位置,并保持“挂起”磁贴的优先级队列
如果瓷砖大于X,请将其扔掉
如果瓷砖已经在可触及的区域内,请将其扔掉
如果此时无法访问该磁贴,请将其添加到挂起队列
如果磁贴扩展了可访问区域,请执行此操作,并重新处理挂起队列中当前可访问的最近磁贴,或者在此重新处理过程中变得可访问
(如果现在可以到达X,则停止)
每个磁贴最多处理两次,每次处理0(1)步,除了在小整数优先级队列中添加和删除min的成本外,还有专门的算法-请参阅。[Python中的此解决方案类似于mcdowella的解决方案;但它不使用优先级队列,而是使用大小为X的数组作为最多X的位置。它的复杂性是O(N+min(N,X)*D)
,因此它不是真正的线性,而是N中的线性…]
数组world
跟踪位置1,…,X-1。通过跳到最远可达的磁贴,每个磁贴都会更新当前位置
def jumpAsFarAsPossible(currentPos, D, X, world):
for jump in range(D,0,-1): # jump = D,D-1,...,1
reachablePos = currentPos + jump
if reachablePos >= X:
return X
if world[reachablePos]:
return jumpAsFarAsPossible(reachablePos, D, X, world)
return currentPos
def solve(X,D,A):
currentPos = 0
# initially there are no tiles
world = X * [False]
for k,tilePos in enumerate(A):
if tilePos < X:
world[tilePos] = True
# how far can we move now?
if currentPos+D >= tilePos:
currentPos = jumpAsFarAsPossible(currentPos, D, X, world)
# have we reached X?
if currentPos == X:
return k # success in k-th second
return -1 # X could not be reached
def跳线可能(当前位置、D、X、世界):
对于范围(D,0,-1)内的跳跃:#跳跃=D,D-1,…,1
可达位置=当前位置+跳转
如果可达位置>=X:
返回X
如果世界[可到达位置]:
返回跳线尽可能(可到达位置、D、X、世界)
返回电流位置
def解算(X,D,A):
currentPos=0
#最初没有瓷砖
world=X*[错误]
对于k,枚举(A)中的tilePos:
如果tilePos=tilePos:
currentPos=可能的跳线(currentPos、D、X、world)
#我们到X了吗?
如果currentPos==X:
第k秒返回k#成功
返回-1#X无法访问
下面是另一种尝试:
创建一个大小为X的数组B。初始化它
def jumpAsFarAsPossible(currentPos, D, X, world):
for jump in range(D,0,-1): # jump = D,D-1,...,1
reachablePos = currentPos + jump
if reachablePos >= X:
return X
if world[reachablePos]:
return jumpAsFarAsPossible(reachablePos, D, X, world)
return currentPos
def solve(X,D,A):
currentPos = 0
# initially there are no tiles
world = X * [False]
for k,tilePos in enumerate(A):
if tilePos < X:
world[tilePos] = True
# how far can we move now?
if currentPos+D >= tilePos:
currentPos = jumpAsFarAsPossible(currentPos, D, X, world)
# have we reached X?
if currentPos == X:
return k # success in k-th second
return -1 # X could not be reached
if (X/D > N+1) return -1; // Taking care of rounding is left as an exercise
Let C = array of chunks as described above
For each C[k] // This is the O(X/D) part
{
Let C[k].problematic = true
Let C[k].left = -1
Let C[k].right = -1
}
Let problematicCounter = number of elements in array C
Let C[k] be the chunk that contains position 0 (usually the first one, but I'll leave open the possibility of "sentinel" chunks)
Let C[k].problematic = false
Let C[k].left = 0
Let C[k].right = 0
Decrement problematicCounter
// The following steps would require tweaking if there is one single chunk on the track
// I do not consider that case as that would imply D >= 2*N, which is kind of ridiculous for this problem
Let C[k] be the chunk containing position X (the last position on the track)
Let C[k].problematic = false
Let C[k].left = X
Let C[k].right = X
Decrease problematicCounter
// Initialization done. Now for the loop.
// Everything inside the loop is O(1), so the loop itself is O(N)
For each A[i] in array A (the array specifying which tile to place in second i)
{
Let C[k] be the chunk containing position A[i]
If C[k].problematic == true
{
Let C[k].problematic = false;
Decrement problematicCounter
}
If C[k].first == -1 OR C[k].first > A[i]
{
Let C[k].first = A[i]
// Checks that C[k-1] and C[k-2] don't go off array index bounds left as an exercise
If C[k-1].problematic == true AND C[k-2].last <> -1 AND C[k].first - C[k-2].last <= D
{
Let C[k-1].problematic = false
Decrement problematicCounter
}
If C[k].last == -1 OR C[k].last < A[i]
{
Let C[k].last = A[i]
// Checks that C[k+1] and C[k+2] don't go off array index bounds left as an exercise
If C[k+1].problematic == true AND C[k+2].first <> -1 AND C[k+2].first - C[k].last <= D
{
Let C[k+1].problematic = false
Decrement problematicCounter
}
If problematicCounter == 0 Then return k // and forget everything else
}
return -1