Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day427 #862

Merged
merged 2 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,4 +422,5 @@ problems from
* [Day 423](https://github.com/vaskoz/dailycodingproblem-go/issues/854)
* [Day 424](https://github.com/vaskoz/dailycodingproblem-go/issues/856)
* [Day 425](https://github.com/vaskoz/dailycodingproblem-go/issues/858)
* [Day 427](https://github.com/vaskoz/dailycodingproblem-go/issues/861)

90 changes: 90 additions & 0 deletions day427/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package day427

import (
"errors"
)

// Elevation is a map of locations to their elevations.
type Elevation map[int]int

// PathDistance represents the distance for a particular path.
type PathDistance map[int]map[int]int

// MaxInt is the largest int.
const MaxInt = int(^uint(0) >> 1)

var errNotPossible = errors.New("no path possible")

// ErrNotPossible is the error returned if a path doesn't exist.
func ErrNotPossible() error {
return errNotPossible
}

// ShortestRunnerPath with the condition that the route goes entirely
// uphill at first, and then entirely downhill.
// Always starts at location 0 and end at location 0.
func ShortestRunnerPath(elevations Elevation, paths PathDistance) (int, error) {
min := MaxInt
startElev := elevations[0]

for next, distance := range paths[0] {
nextElev := elevations[next]
if nextElev > startElev {
dist, err := shortestRunnerPath(elevations, paths, map[int]struct{}{}, true, next)
if err == nil && dist+distance < min {
min = dist + distance
}
}
}

if min == MaxInt {
return 0, ErrNotPossible()
}

return min, nil
}

func shortestRunnerPath(
elevations Elevation,
paths PathDistance,
visited map[int]struct{},
up bool,
n int) (int, error) {
if n == 0 {
return 0, nil
}

min := MaxInt
currentElevation := elevations[n]

for next, distance := range paths[n] {
if _, visit := visited[next]; !visit {
nextElevation := elevations[next]
if up && nextElevation > currentElevation {
visited[next] = struct{}{}
dist, err := shortestRunnerPath(elevations, paths, visited, true, next)
totalDist := dist + distance

if err == nil && totalDist < min {
min = totalDist
}

delete(visited, next)
} else if nextElevation < currentElevation {
visited[next] = struct{}{}
dist, err := shortestRunnerPath(elevations, paths, visited, false, next)
totalDist := dist + distance
if err == nil && totalDist < min {
min = totalDist
}
delete(visited, next)
}
}
}

if min == MaxInt {
return 0, ErrNotPossible()
}

return min, nil
}
145 changes: 145 additions & 0 deletions day427/problem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package day427

import "testing"

// nolint
var testcases = []struct {
elevations Elevation
paths PathDistance
shortestRoute int
err error
}{
{
Elevation{
0: 15,
1: 10,
2: 25,
},
PathDistance{
0: {
2: 5,
},
1: {
0: 1,
},
2: {
1: 7,
},
},
0,
ErrNotPossible(),
},
{
Elevation{
0: 10,
1: 25,
2: 15,
},
PathDistance{
0: {
1: 15,
2: 5,
},
1: {
0: 1,
},
2: {
1: 7,
0: 30,
},
},
13,
nil,
},
{
Elevation{
0: 10,
1: 5,
2: 7,
},
PathDistance{
0: {
1: 5,
2: 10,
},
1: {
0: 5,
},
2: {
0: 10,
},
},
0,
ErrNotPossible(),
},
{
Elevation{
0: 10,
1: 5,
2: 15,
},
PathDistance{
0: {
1: 5,
2: 10,
},
1: {
0: 5,
},
2: {
0: 10,
},
},
20,
nil,
},
{
Elevation{
0: 5,
1: 25,
2: 15,
3: 20,
4: 10,
},
PathDistance{
0: {
1: 10,
2: 8,
3: 15,
},
1: {
3: 12,
},
2: {
4: 10,
},
3: {
4: 5,
0: 17,
},
4: {
0: 10,
},
},
28,
nil,
},
}

func TestShortestRunnerPath(t *testing.T) {
t.Parallel()

for _, tc := range testcases {
if result, err := ShortestRunnerPath(tc.elevations, tc.paths); result != tc.shortestRoute || err != tc.err {
t.Errorf("Expected (%v,%v), got (%v,%v)", tc.shortestRoute, tc.err, result, err)
}
}
}

func BenchmarkShortestRunnerPath(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testcases {
ShortestRunnerPath(tc.elevations, tc.paths) // nolint
}
}
}