Пожалуйста, прочтите новое правило об ограничении использования AI-инструментов. ×

LAZY solution to problem D in Codeton round 8

Правка en2, от drdilyor, 2024-03-30 21:58:05

Heyo! If you tried to solve problem D you have probably ended up trying to optimize this algorithm:

C++ brute force

But of course this won't work! The number of values in dp[i] will grow exponentially. But we only need to output first K values of the final dp[0]. Can we do better?

We only need to store first K values for each dp[i]. So we can rewrite: dp[i] = cur; while (dp[i].size() > k) dp[i].pop_back();

This leaves us with O(n^2 k) solution, this time the bottleneck is the merging part. The OFFICIAL solution is to use priority queue to select first K values after merging, without going through all values. But we are not interested in that!

LAZY solution

If you have heard about Haskell, you might know that it's a lazy language, it only computes things when it absolutely needs them -- e.g. when you print them. Could it be the case that just switching our programming language to Haskell make the solution pass? (Spoiler: yes, kindof)

Skeleton of Haskell code

code

The code above reads the input into 2D array arr such that we can access element as arr ! (i, j).

Now we need the merge function, it takes 2 sorted lists and returns merged one:

merge :: [Int] -> [Int] -> [Int]
merge (x : xs) (y : ys) =
  if x > y
    then x : merge xs (y : ys)
    else y : merge (x : xs) ys
merge [] xs = xs
merge xs [] = xs

Pretty simple if you know the syntax, but it's not very important.

Now to rewrite the core of the algorithm above (it's possible to do this without recursion but it's less readable):

  let
    calc dp (-1) = head dp
    calc dp i = calc (curdp : dp) (i-1)
      where
        prepareMerge j dpval =
          if j < i then dpval
          else map (+ (arr ! (i, j))) dpval -- add arr[i][j] to every element
        tofold = zipWith prepareMerge [(i-1)..] dp
        curdp = ...
  putInts $ calc [[0], [0]] (n-1)

where clause declares variables: tofold holds all the lists that we want to merge. Now consider:

curdp = foldr merge [] tofold

foldr function applies merge to every element from right to lest: merge(tofold[0], merge(tofold[1], ... merge(tofold[m-1], [])))

Now our submission 254204032 confidently passes in 1200ms

How easy was that?

The catch

I have no idea how the above works. It should've worked in O(n*n*k) but it looks like working in O(n*n + k), maybe that's the editorial bonus solution?

Anyways,

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en15 Английский drdilyor 2024-04-01 08:07:35 5 fix typos
en14 Английский drdilyor 2024-03-31 17:21:32 151
en13 Английский drdilyor 2024-03-30 23:00:25 41 Tiny change: ' showing `$` as `$$$`, I don'' -> ' showing `\$` as `\$\$\$`, I don''
en12 Английский drdilyor 2024-03-30 22:56:18 29
en11 Английский drdilyor 2024-03-30 22:36:29 86
en10 Английский drdilyor 2024-03-30 22:35:32 6484
en9 Английский drdilyor 2024-03-30 22:35:26 6484 Tiny change: '' -> 'Heyo'
en8 Английский drdilyor 2024-03-30 22:33:20 9 Tiny change: 'putInts $ calc [[' -> 'putInts $ take k $ calc [['
en7 Английский drdilyor 2024-03-30 22:31:38 4 Tiny change: 'ion:254204032] confiden' -> 'ion:254204408] confiden'
en6 Английский drdilyor 2024-03-30 22:29:28 87
en5 Английский drdilyor 2024-03-30 22:24:36 1889 (published)
en4 Английский drdilyor 2024-03-30 22:18:07 885
en3 Английский drdilyor 2024-03-30 22:06:54 585
en2 Английский drdilyor 2024-03-30 21:58:05 6236 Tiny change: '):\n\n```haskell\n let\n ' -> '):\n\n```hs\n let\n '
en1 Английский drdilyor 2024-03-30 21:27:53 2038 Initial revision (saved to drafts)