在面试中我被问到这个问题。
你站在0,你必须到达一个位置X.你可以跳到D(1到D)。如果X> D,很明显你在初始跳跃时无法到达位置X.
现在,每秒从1到N出现在随机位置的瓦片。这是作为零索引阵列A [k]给出的,其中A [k]表示出现在第k秒的瓦片的位置。你必须找到,在那时你可以到达(或越过)目的地X.
如果可能在初始时或在A [0]之后,则返回0,或返回最小秒。如果即使在所有瓷砖之后也不可能,则返回-1。
约束: 1 <= N <= 100,000
1 <= D <= 100,000
1 <= X <= 100,000
1&lt; = A [i]&lt; = X
例如
X = 7,D = 3
A = {1,3,1,4,2,5}
然后回答是3.因为在第3个第二个图块出现在位置4并且可以达到X = 7。在此之前的任何一秒都不可能。
我知道这是一个措辞太多的问题,但如果我无法沟通,我绝对可以清除任何问题。
问题在于,预期的时间复杂度为O(N),您可以使用额外的空间O(X)。
我找到了一个O(n * log n * log n)的解决方案。这是二次搜索并获得第一个[1..mid]元素,按位置排序并验证解决方案。它似乎通过了测试用例,但它不是线性的。
我努力但找不到任何O(N)解决方案。你能帮我么?
答案 0 :(得分:0)
线性是指瓷砖数量的线性,对吗?
如果是这样,这个解决方案(在Java中)只迭代一次tile数组。
在每次迭代中,它还需要迭代D和X次,但它相对于tile数组的大小是线性的。
如果它听起来与您正在寻找的相似,请告诉我。
注意:为简化起见,我假设位置“0”的图块在第二个数字“0”处可用,因此有效地将第二个“0”视为仅存在您所在图块的时间,然后其他图块出现在秒1,2等处
public class TestTiles {
public static int calculateSecond(Integer x, Integer d, int[] tiles) {
// start by making all positions unreachable (false)
boolean[] posReachable = new boolean[x+1];
// iterate each second only once
for (int second = 0; second < tiles.length; second++) {
int tile = tiles[second]; // this tile is available now
// so mark all positions from "tile" to "tile + d" reachable
for (int pos = tile; (pos <= tile + d) && pos <= x; pos++) {
posReachable[pos] = true;
}
// are all positions reachable now? if so, this is the second to return
boolean reachable = true;
for (int pos = 0; pos <= x; pos++) {
reachable &= posReachable[pos];
}
if (reachable) return second;
}
// we can't reach the position
return -1;
}
public static void main(String[] args) {
System.out.println(calculateSecond(7, 3, new int[]{0,1,3,1,4,2,5}));
System.out.println(calculateSecond(20, 3, new int[]{0,1,3,1,4,2,5}));
System.out.println(calculateSecond(2, 3, new int[]{0,1,3,1,4,2,5}));
System.out.println(calculateSecond(4, 3, new int[]{0,1,3,1,4,2,5}));
System.out.println(calculateSecond(15, 3, new int[]{0,12,3,9,6}));
}
}
答案 1 :(得分:0)
以下提案应花费时间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的结果间隔。当我们的地图为空时,我们就完成了。 / p>
Haskell中的完整实现如下。
import Data.Ix
import Data.Map
import qualified Data.Map as M
-- setup: the initial set of intervals is just the singleton from 0 to x
search :: Int -> Int -> [Int] -> Maybe Int
search d x = search_ d (insertCandidate d 0 x empty)
search_ :: Int -> Map Int Int -> [Int] -> Maybe Int
search_ d = go where
-- first base case: we've found all the paths we care about
go intervals _ | M.null intervals = Just 0
-- second base case: we're out of tiles, and still don't have all the paths
go _ [] = Nothing
-- final case: we need to take a time step. add one, and recursively search the remaining time steps
go intervals (tile:tiles) = (1+) <$> go newIntervals tiles where
newIntervals = case lookupLE tile intervals of
Just (lo, hi) | inRange (lo, hi) tile
-> insertCandidate d lo tile
. insertCandidate d tile hi
. delete lo
$ intervals
_ -> intervals
-- only keep non-trivial intervals
insertCandidate :: Int -> Int -> Int -> Map Int Int -> Map Int Int
insertCandidate d lo hi m
| hi - lo <= d = m
| otherwise = insert lo hi m
在ghci中运行此示例的一些示例(我在其他答案中无耻地删除了示例):
> search 3 7 [1,3,1,4,2,5]
Just 4
> search 3 20 [1,3,1,4,2,5]
Nothing
> search 3 2 [1,3,1,4,2,5]
Just 0
> search 3 4 [1,3,1,4,2,5]
Just 1
> search 3 15 [12,3,9,6]
Just 4
答案 2 :(得分:0)
我会逐个处理磁贴,因为它们在数组中,跟踪最大可达位置,并保持优先级队列&#34; pending&#34;瓦片。
除了从小整数优先级队列中添加和删除min的成本之外,每个处理最多两次,每次处理O(1)步,其中有专门的算法 - 请参阅{{3}为此。
答案 3 :(得分:0)
[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
答案 4 :(得分:0)
这是另一次尝试:
创建一个大小为X的数组B.将其初始化为MAX_VALUE,然后填入元素B [A [i]] = min(B [A [i]],i),使B的每个元素都是巨大的或第一次在该广场上出现一块瓷砖。
将当前时间初始化为零,并从左向右沿B工作,尝试使用最多D的切片跳过0跳到X,使用不大于当前时间的B元素。如果你不能再继续下去,可以将当前时间增加到B中任何允许你跳得更远的方格中找到的最小值。
成本为O(X log(D))+ O(N) - 您通过一次成本O(N)A初始化X,然后一步一步地沿X行进。如果你保留一个优先级队列来覆盖每个时间点的X中的下一个D元素,你可以找到成本不超过log(D)的X的最小可到达元素 - 并且这些是小整数,所以你可能能够做得更好。