Finding maximum paths

12

Given a square of positive, natural numbers write a program find a horizontal and a vertical path with the sum of numbers along them being maximal. A horizontal path goes from the first column to the last and has to increase its column position by one in each step. A vertical path goes from the first row to the last and has to increase its row position by one in each step. Furthermore the row position in a horizontal path may stay the same or change by one in either direction, likewise for vertical paths.

To illustrate, the following could be a valid path:

Illustration of a valid path

The following path would be invalid, since it steps backwards (and remains on the same row in some places):

Illustration of an invalid path

The following path would be equally invalid, since it changes the row position by more than one in a single step:

Another illustration of an invalid path

Note: The solution should run in an acceptable amount of time.

Input

n lines of input with n space-separated positive integers each are given on standard input. 2 ≤ n ≤ 40. Every line is terminated by a line break. The numbers are small enough that the maximum sum fits in a 32-bit signed integer.

Output

The maximum sums of the horizontal and vertical paths (in that order) separated by a single space.

Sample input 1

1 2
1 2

Sample output 1

3 4

Sample input 2

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2 1 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 4 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 4 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Sample output 2

37 35

Sample input 3

683 671 420 311 800 936
815 816 123 142 19 831
715 588 622 491 95 166
885 126 262 900 393 898
701 618 956 865 199 537
226 116 313 822 661 214

Sample output 3

4650 4799

For your convenience we have prepared a few test cases in bash (thanks to Ventero) and PowerShell which you can run your program through. Invocation is: <test> <command line>, so something like ./test python paths.py or ./test.ps1 paths.exe. Have fun :-)

Joey

Posted 2011-03-20T19:47:44.717

Reputation: 12 260

@Joey: Slightly altered task from one we used last year in our contest :) – Joey – 2011-03-21T16:18:30.560

+10 for the bash test script! I wish all code golf came with such. – MtnViewMark – 2011-03-24T02:13:11.980

@MtnViewMark: We try :-) Personally I hate tasks that require too much clarification after being posted and I usually write my own test scripts anyway since I need to know when an attempt to golf further introduces a regression. Also I have observed that some people tend to post plainly wrong answers. Test cases help getting everyone on the same line. Having a facility that works for every task instead of just one-off hackjobs for each task would clearly be better, but we're not quite there yet ;-) – Joey – 2011-03-24T10:20:35.337

Answers

6

GolfScript - 49 Nabb enhanced characters

51 characters
50 strictly and utterly necessary characters + 3 slackers that only did the job of 1
56 mostly redundant characters

n%{~]}%.zip{{0@+\{\.1>\3<$-1=@+}%\;}*$-1=\}2*' '@

51 solution:

n%{~]}%.zip{(\{0@+\{\.1>\3<$-1=@+}%\}%;$-1=\}2*' '@

53 solution:

n/{~]}%);.zip{(\{0@+\{\.1>\3<$-1=@+}%\}%;$-1=\}2*' '@
             a8_b9___c10___11______12 13      14_

The method works on two lines at a time, one containing the maximum sums reached at each point, and one containing the next line.

a/14: Repeat twice, once for each result.
8: Take the first line from the input and switch it behind the input array, this in now the first set of maximum sums.
b/13: Iterate over each remaining line in the array.
9: Put 0 at the beginning of the maximum sums.
c/12: Iterate over each element of the line.
10: Make a copy of the maximum sums with the first element removed.
11: Take the first 3 elements of the maximum sums, sort them and add the largest to the current element of the line.

56 solution:

n/{~]}%);.zip{1,99*\{{\.1>\3<$-1=@+}%0\+\}%;$-1=\}2*' '@
1________2___ 3____ 4______________________5_____ 6_7___

1: From input to array of arrays in 9 characters, actually it could have been done with just 1, but I broke that key so this will have to do.
2: 4 characters just to make a transposed copy.
3: Array of 99 0s in 5 characters, it could probably be done in a way smarter fashion, but I smoke too much weed to figure how.
4: Overly complicated double loop that iterate over every single element of the input and does some fuzzy logic or something like that to produce the result. Nabb will probably make something equivalent in around 3½ characters.
5: By now the result is there, inside an array that is, this silly piece of code is just there to get it out (and junk a piece of leftovers (and switch the result into place)).
6: This is a command so simple that its character count would probably be negative in an optimal solution. 7: At this point the program is really done, but due to sloppiness in the preceding code the output is in the wrong order and lacks a space, so here goes a few more bits down the drain.

aaaaaaaaaaaa

Posted 2011-03-20T19:47:44.717

Reputation: 4 365

Ahh, I just accidently assumed the input not to end with a newline. I'm surprised that it actually worked partially, that kind of stuff usually mess up a GolfScript program completely. – aaaaaaaaaaaa – 2011-03-21T15:57:59.210

1Looks fine, although you should be using {}* instead of (\{}%. – Nabb – 2011-03-23T08:13:42.800

Yes that makes sense, thanks. – aaaaaaaaaaaa – 2011-03-23T14:22:54.900

3

Haskell: 314 necessary characters

import Data.Vector(fromList,generate,(!))
import List
l=fromList
x=maximum
g=generate
p a=show$x[m!i!0|i<-[0..h-1]]where{
w=length$head a;h=length$a;n=l$map l a;
m=g h$ \i->g w$ \j->n!i!j+x[k#(j+1)|k<-[i-1..i+1]];
i#j|i<0||i>=h||j>=w=0|1>0=m!i!j;}
q a=p a++' ':(p.transpose)a
main=interact$q.map(map read.words).lines

Note: this requires the module Data.Vector. I'm not sure if it's included in the Haskell platform or not.

Ungolfed version:

import Data.Vector(fromList,generate,(!))
import Data.List

-- horizontal; we use transpose for the vertical case
max_path :: [[Integer]] -> Integer
max_path numbers = maximum [m ! i ! 0 | i <- [0..h-1]] where
    w = length (head numbers)
    h = length numbers
    n = fromList $ map fromList numbers
    m = generate h $ \i -> generate w $ \j ->
        n ! i ! j + maximum [f i' (j+1) | i' <- [i-1..i+1]]
    f i j | i < 0 || i >= h || j >= w = 0
    f i j = m ! i ! j

max_paths :: [[Integer]] -> String
max_paths numbers = (show . max_path) numbers ++ " " ++
                    (show . max_path . transpose) numbers

main = interact $ max_paths . map (map read . words) . lines

This solution uses laziness, in tandem with Data.Vector, for memoization. For every point, the solution for the maximum path from it to the end is computed, then stored in the cell of Vector m and reused when needed.

Joey Adams

Posted 2011-03-20T19:47:44.717

Reputation: 9 929

I guess you can strip the curly braces after your where statement, if you collapse all the definitions into one single line. – FUZxxl – 2011-03-21T17:59:24.303

3

J, 91 95

a=:".;._2(1!:1)3
c=:4 :'>./x+"1|:y,.(1|.!.0 y),._1|.!.0 y'
p=:[:>./c/
(":(p|:a),p a)1!:2(4)

I decline to do IO, lowering my score dramatically. Passes all tests in the test harness (though it only works if the input ends with a line ending, as in the test harness).

I removed the handling for Windows line endings, since Chris suggested that it wasn't necessary. The multi-platform version would have a=:".;._2 toJ(1!:1)3 as the first line.

Explanation:

  • f gives the solution pair by calling p normally and with input transposed (|:).
  • p takes the maximum (>./) of the row-totals from applying c between each row (c/)
  • c takes two rows (x and y). It adds x to each of y, y shifted up 1 cell (1|.!.0 y), and y shifted down 1 cell (_1|.!.0 y). Then it takes the maximums of the three alternatives for each row. (>./). The rest is rank [sic] - I'm not sure if I'm doing it right.

Jesse Millikan

Posted 2011-03-20T19:47:44.717

Reputation: 1 438

4Exactly, lowering your score. -1 – aaaaaaaaaaaa – 2011-03-22T12:16:34.403

@eBusiness: Are you sure downvoting is the right response to an incomplete solution? – Jesse Millikan – 2011-03-22T20:01:32.313

1@Joey: Not upvoting is the other option. I was too tired to do IO at the time, but my solution is so different from the other J solution that I really wanted to post it anyhow. If there was an explicit way to mark the answer as "non-participating", or something like that, I would have. – Jesse Millikan – 2011-03-22T20:09:05.397

@Joey: Another reason is that down-votes are unlikely to be reversed even if the solution is fixed; the original user has to come back and change their vote. (Deleted, realized that short-circuited the discussion, and undeleted. I guess I'll shoot for the "Disciplined" badge instead.) – Jesse Millikan – 2011-03-22T20:27:50.263

@Jesse Millikan: We do that. No guarantees, but if you fix the issue within reasonable time most downvoters should revoke their votes. – aaaaaaaaaaaa – 2011-03-22T21:25:44.233

@Jesse: I don't think "non-participating" answers should be encouraged, as that often lowers the S/N ratio for challenge posts. CW is indeed one possibility, but I'll be starting a discussion about whether non-golf answers to [code-golf] threads should be permitted at all---and if it's agreed that they're bad to have, I'll start actively deleting such answers. – Chris Jester-Young – 2011-03-22T21:39:19.017

@Chris: Edit: Will wait until you post something on meta to comment on that. Also, note that my answer was a wrong golf answer, not a non-golf answer :) – Jesse Millikan – 2011-03-22T21:49:12.020

@Chris, @Joey, @eBusiness: Fixed IO. Thanks for the... Clarification, or whatever that was. Meanwhile, can someone answer me whether we're expected to account for both Windows and Unix line endings? – Jesse Millikan – 2011-03-22T23:20:41.967

Also, my most sincere apologies to anyone trying to read edit histories of my answers. I'm edit-crazy. – Jesse Millikan – 2011-03-22T23:22:19.343

@Jesse: I withdrew my downvote. I think it's fair to use LF line endings, since those are usually less of a pain to handle than CRLF ones. – Chris Jester-Young – 2011-03-22T23:38:40.627

Since it's not specified in the question I'd say you can use the lineshifts of your choice. – aaaaaaaaaaaa – 2011-03-22T23:46:26.690

That's three or four characters I can certainly live without... – Jesse Millikan – 2011-03-22T23:47:16.520

Jesse: line endings of your choice (or whatever is appropriate on your platform). And the last line of input is indeed terminated by a line break (as stated in the task description under Input). – Joey – 2011-03-23T15:47:14.920

This would be easier if I could read. "I can read readin', but I can't read writin'." – Jesse Millikan – 2011-03-23T16:16:09.037

2

Ruby 1.9, 155 characters

f=->t{(1...l=t.size).map{|a|l.times{|b|t[a][b]+=t[a-1][(b>0?b-1:0)..b+1].max}};t[-1].max};q=[*$<].map{|a|a.split.map &:to_i};puts [f[q.transpose],f[q]]*" ""

Straightforward solution that passes all testcases.

Ventero

Posted 2011-03-20T19:47:44.717

Reputation: 9 842

2

Haskell, 154 characters

import List
z=zipWith
f=show.maximum.foldl1(\a->z(+)$z max(tail a++[0])$z max(0:a)a)
q a=f(transpose a)++' ':f a
main=interact$q.map(map read.words).lines

  • Edit: (155 -> 154) inlined the function folded over

MtnViewMark

Posted 2011-03-20T19:47:44.717

Reputation: 4 779

Will using zipWith3 shorten the code? – proud haskeller – 2014-08-24T22:54:04.220

I think you could replace maximum with foldl1 max , which adds characters but allows you to factor out foldl1 and max, which should save characters. – proud haskeller – 2014-08-24T22:58:34.113

maximum.foldl1, max, and max --vs-- f=foldl1;m=max;, f m.f, m, and m. -- or 20 vs. 22. So, no, it doesn't save. – MtnViewMark – 2014-08-27T14:55:45.017

Right. And i just remembered the monomorphism restriction would stop writing m=max. What about zipWith3? – proud haskeller – 2014-08-27T15:06:51.610

1

J, 109+10= 119 chars

y=:0".(1!:1)3
N=:%:#y
y=:y$~N,N
r=:(((1&{)(+(3>./\0,0,~]))(0&{)),2&}.)^:(<:N)
(":([:>./"1([:r|:),r)y)(1!:2)4

Run with tr:

cat << EOF | tr \\n ' ' | ./maxpath.ijs

As usual in J, most of the code is for input/output. The "actual" code is 65 characters:

r=:(((1&{)(+(3>./\0,0,~]))(0&{)),2&}.)^:(<:#y)
([:>./"1([:r|:),r)y

Passes all test cases

Eelvex

Posted 2011-03-20T19:47:44.717

Reputation: 5 204

So we need JB again with a solution that reduces the parsing to 10 characters? ;-) – Joey – 2011-03-21T08:52:56.873

@Joey I'm on holiday, I barely have internet access; not much time for golfing ;-) – J B – 2011-03-21T10:02:32.603

Can you clue me in on how you're directly running maxpath.ijs? – Jesse Millikan – 2011-03-22T22:28:51.673

@Jesse: In *nix put some #!/usr/bin/env jconsole on top of the file and set the executable flag. – Eelvex – 2011-03-22T22:47:31.463

1

Python, 204 characters

import sys
I=sys.stdin.read()
n=I.count('\n')
A=map(int,I.split())
R=range(n)
X=lambda h,a:[a[i]+max(([0]+h)[i:i+3])for i in R]
h=v=[0]*n
for i in R:h=X(h,A[i*n:i*n+n]);v=X(v,A[i::n])
print max(v),max(h)

Keith Randall

Posted 2011-03-20T19:47:44.717

Reputation: 19 865

1

Python, 149

import sys
def f(g,t=[]):
 for r in g:t=[int(e)+max(t[i-1:i+2]+[0])for i,e in enumerate(r)]
 print max(t),
g=map(str.split,sys.stdin)
f(zip(*g)),f(g)

If I were to calculate only a vertical or horizontal shortest path,
it could be done in-place instead, saving about a third of the bytes.

hallvabo

Posted 2011-03-20T19:47:44.717

Reputation: 1 640