RogerBW's Blog

The Weekly Challenge 270: Special Distribtions Position the Elements 26 May 2024

I’ve been doing the Weekly Challenges. The latest involved searching matrices and raising array elements. (Note that this ends today.)

Task 1: Special Positions

You are given a m x n binary matrix.

Write a script to return the number of special positions in the given binary matrix.

A position (i, j) is called special if $matrix[i][j] == 1 and all other elements in the row i and column j are 0.

I got whimsical on this one.

The simplistic approach would obviously be to scan for 1s and then scan each row and column in which the occurred.

Instead, I validate rows to get a list of potential (x, y) points, then validate columns for each of the x values to see which ones are still valid. Then iterate the list of points and count the ones with valid x.

The "full" version needs something like a tuple to act as a set key. For languages that don't offer this (Perl, PostScript, Lua) I put the (y, x) pairs in a list instead, but since the order doesn't matter a set feels like a more appropriate structure for the purpose.

Perl:

The validator answers "is this sequence all zeroes except for a 1, and if so where is the 1".

sub validator($a0) {
  my @a = sort @{$a0};
  my $l = scalar @a;
  if ($a[0] == 0 && $a[$l - 2] == 0 && $a[$l - 1] == 1) {
    foreach my $i (0 .. $l - 1) {
      if ($a0->[$i]== 1) {
        return $i;
      }
    }
  }
  return -1;
}

sub specialpositions($a) {
  my @vr = ();
  my %xs = ();

Validate each row, and store the potential special position (if any) and the x value.

  while (my ($y, $row) = each @{$a}) {
    my $x = validator($row);
    if ($x > -1) {
      push @vr, [$y, $x];
      $xs{$x} = 1;
    }
  }

Check each column that has a potential special position and delete the ones that don't.

  foreach my $x (keys %xs) {
    keys @{$a};
    my @c = (map {$_->[$x]} @{$a});
    if (validator(\@c) == -1) {
      delete $xs{$x};
    }
  }

Read back the list of positions, and count them.

  return scalar grep {exists $xs{$_->[1]}} @vr;
}

Kotlin, with the set keyed on a Pair (and with its built-in list searcher, shared with Scala, JavaScript and Python):

fun validator(a0: List<Int>): Int {
    val a = a0.sorted()
    val l = a.size
    if (a[0] == 0 && a[l - 2] == 0 && a[l - 1] == 1) {
        return a0.indexOf(1)
    } else {
        return -1
    }
}

fun specialpositions(a: List<List<Int>>): Int {
    var vr = mutableSetOf<Pair<Int, Int>>()
    var xs = mutableSetOf<Int>()
    a.forEachIndexed{y, row ->
                         val x = validator(row)
                     if (x > -1) {
                         vr.add(Pair(y, x))
                         xs.add(x)
                     }
    }
    var xd = mutableSetOf<Int>()
    for (x in xs) {
        val c = a.map{ it[x] }.toList()
        if (validator(c) == -1) {
            xd.add(x)
        }
    }
    xs.removeAll(xd)
    return vr.filter{xs.contains(it.second)}.size
}

Task 2: Distribute Elements

You are give an array of integers, @ints and two integers, $x and $y.

Write a script to execute one of the two options:

Level 1: Pick an index i of the given array and do $ints[i] += 1

Level 2: Pick two different indices i,j and do $ints[i] +=1 and $ints[j] += 1.

You are allowed to perform as many levels as you want to make every elements in the given array equal. There is cost attach for each level, for Level 1, the cost is $x and $y for Level 2.

In the end return the minimum cost to get the work done.

I feel sure there should be a quicker way to do this without the full search routine. Ah well. I haven't done DFS in Scala or Crystal before. Even if there is an awful lot of sorting.

Some optimisations are to prune any path that's produced a cost higher than the previously-found minimum cost, or that increments values beyond the highest existing value.

Crystal:

A queue entry:

struct Op
  property a, cost

  def initialize(@a : Array(Int32), @cost : Int32)
  end
end

def equalizearray(a0 : Array(Int32), x : Int32, y : Int32) : Int32?
  a = a0.sort
  limit = a[-1]

Initialise the queue and minimum cost.

  queue = Deque(Op).new
  queue.push(Op.new(a, 0))
  mc : Int32 | Nil = nil

Loop through the queue.

  while queue.size > 0
    op = queue.shift

If the cost is already at least as high as the minimum cost, we don't care about this branch whether it's complete or not.

    if !mc || mc > op.cost

If the branch is complete, store the new minimum cost.

      if op.a[0] == op.a[-1]
        mc = op.cost
      else

"Level 1": add 1 to the lowest number in this copy of the array, and make sure it's no higher than the limit.

        p = op.a.dup
        p[0] += 1
        if p[0] <= limit

If so, sort and produce a new queue entry.

          p = p.sort
          queue.push(Op.new(p, op.cost + x))

If that worked, try the "Level 2" operation as well.

          q = op.a.dup
          q[0] += 1
          q[1] += 1
          if q[1] <= limit
            q = q.sort
            queue.push(Op.new(q, op.cost + y))
          end
        end
      end
    end
  end
  mc
end

Full code on github.

Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.

Search
Archive
Tags 1920s 1930s 1940s 1950s 1960s 1970s 1980s 1990s 2000s 2010s 2300ad 3d printing action advent of code aeronautics aikakirja anecdote animation anime army astronomy audio audio tech base commerce battletech bayern beer boardgaming book of the week bookmonth chain of command children chris chronicle church of no redeeming virtues cold war comedy computing contemporary cornish smuggler cosmic encounter coup covid-19 crime crystal cthulhu eternal cycling dead of winter doctor who documentary drama driving drone ecchi economics en garde espionage essen 2015 essen 2016 essen 2017 essen 2018 essen 2019 essen 2022 essen 2023 essen 2024 existential risk falklands war fandom fanfic fantasy feminism film firefly first world war flash point flight simulation food garmin drive gazebo genesys geocaching geodata gin gkp gurps gurps 101 gus harpoon historical history horror hugo 2014 hugo 2015 hugo 2016 hugo 2017 hugo 2018 hugo 2019 hugo 2020 hugo 2021 hugo 2022 hugo 2023 hugo 2024 hugo-nebula reread in brief avoid instrumented life javascript julian simpson julie enfield kickstarter kotlin learn to play leaving earth linux liquor lovecraftiana lua mecha men with beards mpd museum music mystery naval noir non-fiction one for the brow opera parody paul temple perl perl weekly challenge photography podcast politics postscript powers prediction privacy project woolsack pyracantha python quantum rail raku ranting raspberry pi reading reading boardgames social real life restaurant reviews romance rpg a day rpgs ruby rust scala science fiction scythe second world war security shipwreck simutrans smartphone south atlantic war squaddies stationery steampunk stuarts suburbia superheroes suspense television the resistance the weekly challenge thirsty meeples thriller tin soldier torg toys trailers travel type 26 type 31 type 45 vietnam war war wargaming weather wives and sweethearts writing about writing x-wing young adult
Special All book reviews, All film reviews
Produced by aikakirja v0.1