DEV Community

dev.to staff
dev.to staff

Posted on

4 1

Daily Challenge #105 - High-Sum Matrix Drop

You have a square matrix of unsigned integer values. Say, 5 + 5.
Each row of that matrix contains numbers from 1 to 5 (or to whatever length of the matrix), randomly sorted.

You can move through the matrix only one step at the time - either straight down, left-down or right-down.

Your challenge is to write a code that will start at the middle of the top row, and find the path down with the highest sum.

Example:
For the following matrix, you start in c/0. For this matrix the best path would be: c/0, d/1, d/2, e/3, d/5 which scores 4+4+5+4+5 = 22.

+---+---+---+---+---+---+
|   | a | b | c | d | e |
+---+---+---+---+---+---+
| 0 | 2 | 1 | 4 | 5 | 3 |
+---+---+---+---+---+---+
| 1 | 5 | 2 | 1 | 4 | 3 |
+---+---+---+---+---+---+
| 2 | 1 | 3 | 2 | 5 | 4 |
+---+---+---+---+---+---+
| 3 | 1 | 5 | 2 | 3 | 4 |
+---+---+---+---+---+---+
| 4 | 3 | 2 | 1 | 5 | 4 |
+---+---+---+---+---+---+

Good luck!


This challenge comes from peledzohar here on DEV. Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (5)

Collapse
 
craigmc08 profile image
Craig McIlwrath

I didn't do #90, so I'll put my answer here. This is a really stupid, slow, "just works" algorithm. It checks every single possible path. I guess it's around O(n3)?

(Language is Haskell).

data Move = D | DL | DR deriving (Show) 

type Size = Int
type Row = Int
type Col = Int
type Pos = (Row, Col) 

isInSquare :: Size -> Pos -> Bool
isInSquare n (r, c) = all id
  [ r >= 0
  , r < n
  , c >= 0
  , c < n
  ]

executeMove :: Move -> Pos -> Pos
executeMove D  (r, c) = (r + 1, c) 
executeMove DL (r, c) = (r + 1, c - 1)
executeMove DR (r, c) = (r + 1, c + 1)

validMove :: Size -> Pos -> Move -> Bool
validMove n p m = isInSquare n $ executeMove m p

possibleMoves :: Size -> Pos -> [Move]
possibleMoves n p = filter (validMove n p) [D, DL, DR]

generatePaths :: Size -> Pos -> [[Move]]
generatePaths n p
  | fst p == n - 1 = [[]]
  | otherwise      = possibleMoves n p >>= nextMoves
  where nextMoves :: Move -> [[Move]] 
        nextMoves m = generatePaths n (executeMove m p) >>= \ms -> [m : ms] 

-- Middle spot of an even array is defined as the right middle for simplicity. 
middle n
  | odd n     = n `div` 2
  | otherwise = n `div` 2

access :: [[a]] -> Pos -> a
access xss (r, c) = xss !! r !! c

sumIn :: (Num a) => [[a]] -> Pos -> [Move] -> a 
sumIn xss start path = let ps = scanl (flip executeMove) start path
                       in  sum $ map (access xss) ps 

maxBy :: (Ord b) => (a -> b) -> [a] -> a
maxBy f = foldl1 maxOf 
  where maxOf m x = if (f x > f m) then x else m

bestPath :: (Ord a, Num a) => Size -> [[a]] -> ([Move], a)
bestPath n xss = let start = (0, middle n)
                     paths = generatePaths n start 
                 in  maxBy snd $ zip paths $ map (sumIn xss start) paths
Collapse
 
sardok profile image
sinx

My solution using queue. Should be o(n).


import string

def get_steps(i, j, row, col):
    left = j - 1
    right = j + 1
    down = i + 1

    if down >= row:
        return []

    steps = []
    if left >= 0:
        steps.append((down, left))

    steps.append((down, j))

    if right < col:
        steps.append((down, right))

    return steps

def make_and_fill(row, col, fill):
    ret = []
    for _ in range(row):
        row_ = [fill] * col
        ret.append(row_)

    return ret

def find_max_index(xs):
    max_ = max(xs)
    return xs.index(max_)

def get_path(i, j, backtraces):
    ret = []
    queue = [(i, j)]

    while queue:
        pos = queue.pop(0)
        if pos:
            ret.append(pos)
            i, j = pos
            backtrace = backtraces[i][j]
            queue.append(backtrace)

    ret.reverse()
    ret = [f'{string.ascii_letters[j]}/{i}' for (i, j) in ret]
    return ', '.join(ret)


def solve(xs):
    row, col = len(xs), len(xs[0])
    sums = make_and_fill(row, col, 0)
    sums[0][:] = xs[0]
    backtraces = make_and_fill(row, col, 0)
    queue = [(0, col//2)]

    while queue:
        pos = queue.pop(0)
        i, j = pos
        for step in get_steps(i, j, row, col):
            step_i, step_j = step
            acc = sums[i][j] + xs[step_i][step_j]
            if sums[step_i][step_j] < acc:
                queue.append((step_i, step_j))
                sums[step_i][step_j] = acc
                backtraces[step_i][step_j] = pos

    max_index = find_max_index(sums[-1])
    print(sums[-1][max_index])

    path = get_path(row-1, max_index, backtraces)
    print(path)


if __name__ == '__main__':
    matrix = [
        [2, 1, 4, 5, 3],
        [5, 2, 1, 4, 3],
        [1, 3, 2, 5, 4],
        [1, 5, 2, 3, 4],
        [3, 2, 1, 5, 4],
    ]
    solve(matrix)
Collapse
 
michaeltharrington profile image
Michael Tharrington

Thanks for pointing this out, David! This was a mistake.

Collapse
 
peledzohar profile image
Zohar Peled

Hey guys, Someone got mixed up a bit here. That was the 90# challenge, this one was suppose to be the one I've sent you later, with the bottles... :-)

Collapse
 
michaeltharrington profile image
Michael Tharrington

Ah jeez... my bad here. 🤦‍♂️

You are absolutely right, Zohar!

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay