083 Path sum four ways.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 14 August 2016
  5. # Website: https://github.com/trizen
  6. # https://projecteuler.net/problem=83
  7. # Runtime: 12.369s
  8. # usage: perl problem_083.pl < p083_matrix.txt
  9. use 5.010;
  10. use strict;
  11. use List::Util qw(min max);
  12. my @matrix;
  13. while (<>) {
  14. push @matrix, [split(/,/)];
  15. }
  16. sub draw {
  17. my ($path) = @_;
  18. print "\e[H\e[J\e[H";
  19. my @screen = map {
  20. [map { '.' } @{$_}]
  21. } @matrix;
  22. foreach my $p (@$path) {
  23. my ($i, $j) = @$p;
  24. $screen[$i][$j] = '*';
  25. }
  26. foreach my $row (@screen) {
  27. say join(' ', @{$row});
  28. }
  29. }
  30. my %seen;
  31. sub valid {
  32. not exists $seen{"@_"};
  33. }
  34. my %two_way_cache;
  35. my $end = $#matrix;
  36. sub two_way_path {
  37. my ($i, $j, $k, $l) = @_;
  38. my $key = "$i $j $k $l";
  39. if (exists $two_way_cache{$key}) {
  40. return $two_way_cache{$key};
  41. }
  42. my @paths;
  43. if ($i < $k) {
  44. push @paths, two_way_path($i + 1, $j, $k, $l);
  45. }
  46. if ($j < $l) {
  47. push @paths, two_way_path($i, $j + 1, $k, $l);
  48. }
  49. $two_way_cache{$key} = $matrix[$i][$j] + (min(@paths) || 0);
  50. }
  51. my @stack;
  52. my $sum = 0;
  53. my ($i, $j) = (0, 0);
  54. my $limit = two_way_path(0, 0, $end, $end) + max(map { @$_ } @matrix);
  55. my %min = (sum => 'inf');
  56. while (1) {
  57. undef $seen{"$i $j"};
  58. $sum += $matrix[$i][$j];
  59. my @points;
  60. if ($i >= $end and $j >= $end) {
  61. if ($sum < $min{sum}) {
  62. $min{sum} = $sum;
  63. $min{path} = [keys %seen];
  64. }
  65. @stack ? goto STACK: last;
  66. }
  67. if (not $sum <= $limit or not $sum <= two_way_path(0, 0, $i, $j)) {
  68. goto STACK if @stack;
  69. }
  70. if (not($sum - $matrix[$i][$j] + two_way_path($i, $j, $end, $end) <= $limit)) {
  71. goto STACK if @stack;
  72. }
  73. if ($i > 0 and valid($i - 1, $j)) {
  74. push @points, [$i - 1, $j];
  75. }
  76. if ($j > 0 and valid($i, $j - 1)) {
  77. push @points, [$i, $j - 1];
  78. }
  79. if ($i < $end and valid($i + 1, $j)) {
  80. push @points, [$i + 1, $j];
  81. }
  82. if ($j < $end and valid($i, $j + 1)) {
  83. push @points, [$i, $j + 1];
  84. }
  85. STACK: if (!@points) {
  86. if (@stack) {
  87. my ($s_sum, $s_seen, $s_pos, $s_points) = @{pop @stack};
  88. $sum = $s_sum;
  89. undef %seen;
  90. @seen{@$s_seen} = ();
  91. @points = @$s_points;
  92. ($i, $j) = @$s_pos;
  93. }
  94. else {
  95. last;
  96. }
  97. }
  98. my $min = splice(@points, int(rand(@points)), 1);
  99. if (@points and $sum <= $limit and $sum <= two_way_path(0, 0, $i, $j)) {
  100. my @ok = (
  101. grep {
  102. my $s = ($sum + $matrix[$_->[0]][$_->[1]]);
  103. $s <= $limit and $s <= two_way_path(0, 0, $_->[0], $_->[1])
  104. } @points
  105. );
  106. if (@ok) {
  107. push @stack, [$sum, [keys %seen], [$i, $j], \@ok];
  108. }
  109. }
  110. ($i, $j) = @$min;
  111. }
  112. my @path = map { [split ' '] } @{$min{path}};
  113. draw(\@path);
  114. say "\nMinimum path-sum is: $min{sum}\n";