하스켈, 204 271 바이트
편집 : 볼록 껍질을 목록으로 업데이트하지만 (golfed 버전과 동일한 복잡성으로) "splitLookup x"대신 "split (x + 1)"을 사용하고 Predule과 같은 정규화 된 함수 호출을 제거하여 훨씬 더 골프를 쳤다. 폴드.
이것은 (a, b) 쌍의 목록과 x 값의 목록을 기대 하는 함수 f 를 만듭니다 . 나는 같은 아이디어를 사용하여 APL 제품군의 모든 것에 의해 길이 방향으로 날아갈 것이라고 생각하지만 여기에 간다.
import Data.Map
r=fromListWith max
[]%v=[(0,v)]
i@((p,u):j)%v|p>v#u=j%v|0<1=(v#u,v):i
(a,b)#(c,d)=1+div(b-d)(c-a)
o i x=(\(a,b)->a*x+b)$snd$findMax$fst$split(x+1)$r$foldl'(%)[]$r$zip(fmap fst i)i
f=fmap.o
샘플 사용법 :
[1 of 1] Compiling Main ( linear-min.hs, interpreted )
Ok, modules loaded: Main.
λ> f [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] [1..5]
[11,12,14,16,20]
λ> f [(1,20), (2,12), (3,11), (4,8)] [1..5]
[21,22,23,24,28]
O (n log n) 시간 내에 작동합니다. 분석은 아래를 참조하십시오.
편집 : 다음은 큰 O 분석 및 모든 작동 방식에 대한 설명이 포함 된 ungolfed 버전입니다.
import Prelude hiding (null, empty)
import Data.Map hiding (map, foldl)
-- Just for clarity:
type X = Int
type Y = Int
type Line = (Int,Int)
type Hull = Data.Map.Map X Line
slope (a,b) = a
{-- Take a list of pairs (a,b) representing lines a*x + b and sort by
ascending slope, dropping any lines which are parallel to but below
another line.
This composition is O(n log n); n for traversing the input and
the output, log n per item for dictionary inserts during construction.
The input and output are both lists of length <= n.
--}
sort :: [Line] -> [Line]
sort = toList . fromListWith max
{-- For lines ax+b, a'x+b' with a < a', find the first value of x
at which a'x + b' exceeds ax + b. --}
breakEven :: Line -> Line -> X
breakEven p@(a,b) q@(a',b') = if slope p < slope q
then 1 + ((b - b') `div` (a' - a))
else error "unexpected ordering"
{-- Update the convex hull with a line whose slope is greater
than any other lines in the hull. Drop line segments
from the hull until the new line intersects the final segment.
split is used to find the portion of the convex hull
to the right of some x value; it has complexity O(log n).
insert is also O(log n), so one invocation of this
function has an O(log n) cost.
updateHull is recursive, but see analysis for hull to
account for all updateHull calls during one execution.
--}
updateHull :: Line -> Hull -> Hull
updateHull line hull
| null hull = singleton 0 line
| slope line <= slope lastLine = error "Hull must be updated with lines of increasing slope"
| hull == hull' = insert x line hull
| otherwise = updateHull line hull''
where (lastBkpt, lastLine) = findMax hull
x = breakEven lastLine line
hull' = fst $ x `split` hull
hull'' = fst $ lastBkpt `split` hull
{-- Build the convex hull by adding lines one at a time,
ordered by increasing slope.
Each call to updateHull has an immediate cost of O(log n),
and either adds or removes a segment from the hull. No
segment is added more than once, so the total cost is
O(n log n).
--}
hull :: [Line] -> Hull
hull = foldl (flip updateHull) empty . sort
{-- Find the highest line for the given x value by looking up the nearest
(breakpoint, line) pair with breakpoint <= x. This uses the neat
function splitLookup which looks up a key k in a dictionary and returns
a triple of:
- The subdictionary with keys < k.
- Just v if (k -> v) is in the dictionary, or Nothing otherwise
- The subdictionary with keys > k.
O(log n) for dictionary lookup.
--}
valueOn :: Hull -> X -> Y
valueOn boundary x = a*x + b
where (a,b) = case splitLookup x boundary of
(_ , Just ab, _) -> ab
(lhs, _, _) -> snd $ findMax lhs
{-- Solve the problem!
O(n log n) since it maps an O(log n) function over a list of size O(n).
Computation of the function to map is also O(n log n) due to the use
of hull.
--}
solve :: [Line] -> [X] -> [Y]
solve lines = map (valueOn $ hull lines)
-- Test case from the original problem
test = [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] :: [Line]
verify = solve test [1..5] == [11,12,14,16,20]
-- Test case from comment
test' = [(1,20),(2,12),(3,11),(4,8)] :: [Line]
verify' = solve test' [1..5] == [21,22,23,24,28]