Starting into Perl Weekly Challenge #64.

TASK #1 › Minimum Sum Path

Submitted by: Mohammad S Anwar

Reviewed by: Ryan Thompson

Given an m × n matrix with non-negative integers, write a script to find a path from top left to bottom right which minimizes the sum of all numbers along its path. You can only move either down or right at any point in time.

Thus, your script could output: 21 ( 1 → 2 → 3 → 6 → 9 )

My first thought for this was to do an iterative Shortest-Path solution, like I’ve used for my Ladder Puzzle solver, but I decided that, instead, a recursive solution would be best. Or at least, easiest to implement.

It kinda doesn’t matter, because if we’re looking for the lowest sums of all paths from upper-left to lower-right, we have to take all paths to get there, and with this as the matrix —

[ 1 2 3 ]
[ 4 5 6 ]
[ 7 8 9 ]

— there are a limited number of paths. 1 → 2 → 3 → 6 → 9, 1 → 2 → 5 → 6 → 9, 1 → 2 → 5 → 8 → 9, 1 → 4 → 5 → 6 → 9, 1 → 4 → 5 → 8 → 9, and 1 → 4 → 7 → 8 → 9, specifically, and they’re all five places long. If there were paths that were shorter, and that’s the thing to be looking for, then this might make sense.

But going depth-first instead of breadth-first, and only passing things back when we reach 9, then we can save a lot of record-keeping if we go with recursion.

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use feature qw{ fc postderef say signatures state switch };
no warnings qw{ experimental };

use List::Util qw{sum};

my $matrix = [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], ];

my @solutions = solve_matrix($matrix);

my ($sol) = sort { sum( $a->@*) <=> sum( $b->@* )} @solutions;

say join ' -> ', $sol->@*;

sub solve_matrix ( $matrix, $x = 0, $y = 0, $path = [] ) {
    my @output;
    my @path = $path->@*;
    my $endx = -1 + scalar $matrix->@*;
    my $endy = -1 + scalar $matrix->[0]->@*;
    return if $x > $endx;
    return if $y > $endy;

    push @path, $matrix->[$x][$y];

    return \@path if $x == $endx && $y == $endy;
    push @output, solve_matrix( $matrix, $x,     $y + 1, \@path );    #right
    push @output, solve_matrix( $matrix, $x + 1, $y,     \@path );    #down
    return wantarray ? @output : \@output;
}

I suppose if I did this iteratively, I could tell when we’ve hit the last and do something like @min_path = map {$_} @path if sum(@path) < sum(@min_path), but oh well.

TASK #2 › Word Break

Submitted by: Mohammad S Anwar

You are given a string $S and an array of words @W.

Write a script to find out if $S can be split into sequence of one or more words as in the given @W.

Print the all the words if found otherwise print 0.

I have this. I am not happy.

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use feature qw{ postderef say signatures state switch };
no warnings qw{ experimental };

use Carp;
use JSON;
my $json = JSON->new->canonical->allow_nonref->pretty->space_after;

my @input;
push @input, [ "perlweeklychallenge", "weekly", "challenge", "perl" ];
push @input, [ "perlandraku",         "python", "ruby",      "haskell" ];

for my $i (@input) {
    my $s = shift $i->@*;
    my @w = $i->@*;
    my $out = wordbreak( $s, @w );
    say $out;
}

sub wordbreak ( $s, @w ) {
    my @permutations = permute_array( \@w );
    for my $perm (@permutations) {
        my $str = join '', $perm->@*;
        return join ',', map { qq{"$_"} } $perm->@* if $str eq $s;
    }
    return 0;
}

sub permute_array ( $array ) {
    return $array if scalar $array->@* == 1;
    my @response = map {
        my $i        = $_;
        my $d        = $array->[$i];
        my $copy->@* = $array->@*;
        splice $copy->@*, $i, 1;
        my @out = map { unshift $_->@*, $d; $_ } permute_array($copy);
        @out
    } 0 .. scalar $array->@* - 1;
    return @response;
}

For the given solution, $s = "perlweeklychallenge"; @w = ( "weekly", "challenge", "perl" ), we can make it work with ( "perl", "weekly", "challenge" ) but by the description, I should be able to add "zed" into the input and still have the same output, I think, but instead of pulling out my old permute_array code, I might have to dive into Algorithm::Combinatorics so I can get subarrays.

If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.