CodeJam 2009 Qualification B

Aaaaaand here we are!

So, link to the problem entitled Watersheds:

And it says:

Geologists sometimes divide an area of land into different regions based on where rainfall flows down to. These regions are called drainage basins.

Given an elevation map (a 2-dimensional array of altitudes), label the map such that locations in the same drainage basin have the same label, subject to the following rules.

  • From each cell, water flows down to at most one of its 4 neighboring cells.
  • For each cell, if none of its 4 neighboring cells has a lower altitude than the current cell’s, then the water does not flow, and the current cell is called a sink.
  • Otherwise, water flows from the current cell to the neighbor with the lowest altitude.
  • In case of a tie, water will choose the first direction with the lowest altitude from this list: North, West, East, South.

Every cell that drains directly or indirectly to the same sink is part of the same drainage basin. Each basin is labeled by a unique lower-case letter, in such a way that, when the rows of the map are concatenated from top to bottom, the resulting string is lexicographically smallest. (In particular, the basin of the most North-Western cell is always labeled ‘a’.)

So, looks like a path problem. As a path problem, we need to calculate which route to use based on series of rules, like walking through a map and choosing where to go next. Since usually the same deciosion applies to all “map units”, recursion is your friend. 🙂

To the I/O Rules:


The first line of the input file will contain the number of maps, TT maps will follow, each starting with two integers on a line — H and W — the height and width of the map, in cells. The next H lines will each contain a row of the map, from north to south, each containingW integers, from west to east, specifying the altitudes of the cells.


For each test case, output 1+H lines. The first line must be of the form

Case #X:

where X is the test case number, starting from 1. The next H lines must list the basin labels for each of the cells, in the same order as they appear in the input.

For each problem, there’s an array filled with height values. What we need to do is fill these arrays with letters, starting from ‘a’. The rules are quite simple:

To better explain the rules, I’ll separate the neighboors in two classes: path neighboors and loop neighboors.

  • Path neighboors: map units that belong to the same “route” and are next to each other. The ones we choose to move to.
  • Loop neighboors: map units that our loop tells us to go to. Usually, loops iterate through arrays the same way: through lines, left to right. Our loop neighboor will be the next neighboor which the loop tells us to go.

For each block, do:

  • Check our number. Are we already a letter on the result? If so, move to the loop neighboor, else, go on.
  • Check the 4 neighboors. Is our number bigger than theirs? No? Then we’re a sink. Write our letter on the result, increase the current letter, move to next neighboor. If our number is bigger, keep going… 😉
  • Which neighboor has the lowest number? There’s a tie? Ok, then let’s choose based on the priorities: North, then West, then East and South. If there’s no tie, choose the lowest. Write our letter and go to the chosen neighboor. 🙂

And… here’s the code. It’s old and really bad written, so be careful. Haven’t tested it recently but I guess it works. If it doesn’t, well… tell me! 😀

from numpy import *
from array import *

alphabet = "abcdefghijklmnopqrstuvwxyz"
cur_basin = 0
ma = [['a']]


def rec( m, ma, h, w, l, a, cur_basin):
    lv = 65535
    lh = 0
    lw = 0

    if (l != 0):
        if (m[l-1][a] < lv):
            lh = l-1
            lw = a
            lv = m[lh][lw]
    if (a != 0):
        if (m[l][a-1] < lv):
            lh = l
            lw = a-1
            lv = m[lh][lw]
    if (a != w-1):
        if (m[l][a+1] < lv):
            lh = l
            lw = a+1
            lv = m[lh][lw]
    if (l != h-1):
        if (m[l+1][a] < lv):
            lh = l+1
            lw = a
            lv = m[lh][lw]

    if ma[l][a] < 'a':
        if lv >= m[l][a]:
            ma[l][a] = alphabet[cur_basin]

            if ma[lh][lw] >= 'a':
                ma[l][a] = ma[lh][lw]

                ma[l][a] = alphabet[cur_basin]
                ma[lh][lw] = ma[l][a]
        if lv > m[l][a]:
            if (l != h-1):
                ma,cur_basin = rec(m, ma, h, w, l+1, a, cur_basin)
            elif (a != w-1):
                ma,cur_basin = rec(m, ma, h, w, l, a+1, cur_basin)
            elif (a != 0):
                ma,cur_basin = rec(m, ma, h, w, l, a-1, cur_basin)
            elif (l != 0):
                ma,cur_basin = rec(m, ma, h, w, l-1, a, cur_basin)

        elif lv < m[l][a]:
            if ma[lh][lw] < 'a':
                ma[lh][lw] = ma[l][a]

    return ma,cur_basin

line = raw_input()

cases = int(line)

for i in range(cases):
    header =  str.split(raw_input())
    h = int(header[0])
    w = int(header[1])

    m = zeros((h,w))
    ma = [['a']]

    cur_basin = 0
    maxh= 0

    for l in range(h):
        mapline = str.split(raw_input())
        if l == 0:
        for a in range(len(mapline)):
            m[l][a] = int(mapline[a])
            maxh = max(maxh,m[l][a])

    ma[0][0] = alphabet[cur_basin]

    for l in range(h):
        for a in range(w):
            ma,cur_basin = rec(m, ma, h, w, l, a, cur_basin)

    print "Case #%d:" % int(i+1)
    for l in range(h):
        for a in range(w):
            if a < w-1:
                print "%c" % ma[l][a],
                print "%c" % ma[l][a]


I don’t remember why I decided to use “inverted axis” (at least for me, using “lines, columns” isn’t usual as I rather do “columns, lines” for positioning) and why it’s so bad to read (hurry?).

Well, happy coding, I guess! 😛


Comment this

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s