这个谜题的描述令人困惑。就像,真的很混乱。我至少读了五遍才明白这个问题。
第一部分
对于这个谜题,示例输入如下:
seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4
地图的每一行都是目标起点、源起点和范围长度。例如,如果我们取seed-to-soil map
的第一行:
$line = '50 98 2' ;
[ $destinationStart , $sourceStart , $rangeLength ] = explode ( ' ' , $line ) ;
$sourceRange = range (
$sourceStart ,
$sourceStart + $rangeLength - 1
) ;
$destinationRange = range (
$destinationStart ,
$destinationStart + $rangeLength - 1
) ;
$mapped = array_combine ( $sourceRange , $destinationRange ) ;
$mapped = [
98 => 50 ,
99 => 51 ,
] ;
需要对输入中的每个地图执行此操作。然后获取第一个地图的种子编号,获取值(如果不在范围内,则获取前一个值),依此类推直到最后一个位置值。因此,在上面的示例中,如果种子数为98
,则值为50
。然后取50
并查看soil-to-fertilizer map
中是否存在该值。如果这没有意义,请查看今天难题的代码页的出现。 [1]
以下是我的完整“解决方案”。我循环遍历每个地图的每一行,根据数字生成范围,并将其添加到mapData
数组中。请注意,我使用array_combine
将源值用作键,将目标值用作新值。 array_replace
保留键,与 array_merge 不同, array_merge
会重置键,这在本例中很重要。
$rawData = explode ( "\n\n" , $input ) ;
$seeds = explode ( ' ' , str_replace ( 'seeds: ' , '' , $rawData [ 0 ] ) ) ;
unset ( $rawData [ 0 ] ) ;
$mapData = [ ] ;
foreach ( $rawData as $value ) {
$lines = explode ( "\n" , $value ) ;
$mapName = str_replace ( ' map:' , '' , $lines [ 0 ] ) ;
unset ( $lines [ 0 ] ) ;
$mapData [ $mapName ] = [ ] ;
foreach ( [ $lines ] as $line )
{
[ $destinationStart , $sourceStart , $rangeLength ] = explode ( ' ' , $line ) ;
// generate the ranges for the source and destination
$mapData [ $mapName ] = array_replace ( $mapData [ $mapName ] , array_combine (
range ( $sourceStart , $sourceStart + $rangeLength - 1 ) ,
range ( $destinationStart , $destinationStart + $rangeLength - 1 ) ,
) ) ;
}
}
$locations = [ ] ;
foreach ( $seeds as $seed )
{
// keep track of the current value, starting with the seed
$current = $seed ;
foreach ( $mapData as $mapName => $map )
{
// if there isn't a value set, fallback to the current
$current = $map [ $current ] ?? $current ;
}
$locations [ ] = $current ;
}
echo 'Lowest location number is ' . min ( $locations ) . PHP_EOL ;
这适用于示例输入,考虑到我花了很长时间试图理解这个谜题,这很好。然后我在真实输入上运行它并得到内存不足错误:
Fatal error: Allowed memory size of 536870912 bytes exhausted
此时我实际上还没有查看真正的输入。与样本输入相比,真实输入的数字很大,因此范围生成导致 PHP 内存不足。这意味着我必须重新考虑我的解决方案,或者投入大量的记忆并希望得到最好的结果。
所以我用 64GB 内存运行它: php -d memory_limit=64000M 1.php
,它工作了!或者至少,它跑了。它给了我一个错误的答案。我更好地查看了实际输入中的数字,然后就到此为止了。
两个小时后,我恍然大悟,意识到如何在不需要Frontier 超级计算机 1.102 exaFLOPS 的情况下解决这个问题。我还注意到一些测试代码对其中一个数组进行了切片,因此即使我没有内存问题,我也永远不会得到正确的答案。哎呀。
第一部分 回归
我可以通过生成每个范围的开始和结束来计算种子的索引,而不是为每个数字生成范围:
- $mapData[$mapName] = array_replace($mapData[$mapName], array_combine(
- range($sourceStart, $sourceStart + $rangeLength - 1),
- range($destinationStart, $destinationStart + $rangeLength - 1),
- ));
+ $mapData[$mapName][] = [
+ 'sourceStart' => $sourceStart,
+ 'sourceEnd' => $sourceStart + $rangeLength - 1,
+ 'destinationStart' => $destinationStart,
+ 'destinationEnd' => $destinationStart + $rangeLength - 1,
+ ];
然后计算差值来创建我的索引,然后在下一个“地图”上使用该索引来获取正确的值:
$locations = [ ] ;
foreach ( $seeds as $seed ) {
$current = $seed ;
foreach ( $mapData as $mapName => $mapArray ) {
foreach ( $mapArray as $map )
{
if ( $current >= $map [ 'sourceStart' ] && $current <= $map [ 'sourceEnd' ] ) {
$index = $current - ( int ) $map [ 'sourceStart' ] ;
$current = ( int ) $map [ 'destinationStart' ] + $index ;
break ;
}
}
}
$locations [ ] = $current ;
}
echo 'Lowest location number is ' . min ( $locations ) . PHP_EOL ;
第一部分完成了。我什至不需要给它额外的内存来运行。
第二部分
套用近藤玛丽亚的话:
你的感受是决策的标准——具体来说,知道什么能激发快乐。在编写代码时要确定这一点,关键是一次拿起一点点代码,然后静静地问自己:“这会激发快乐吗?”
就像2015年的《神奇四侠》一样,没有第二部。不是今天,撒旦。
我的代码在 GitHub 上。
-
可能和我一样困惑⤾