main.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log"
  8. "os"
  9. "slices"
  10. )
  11. func trim(l []byte) []byte {
  12. if len(l) == 0 {
  13. return l
  14. }
  15. if l[len(l)-1] != '\n' {
  16. return l
  17. }
  18. return l[:len(l)-1]
  19. }
  20. func main() {
  21. width, height := 0, 0
  22. r := bufio.NewReader(os.Stdin)
  23. // Read input
  24. plat := make([]byte, 0, 256)
  25. for {
  26. line, err := r.ReadBytes('\n')
  27. if errors.Is(err, io.EOF) {
  28. break
  29. }
  30. if err != nil {
  31. log.Fatalf("While reading input: %v", err)
  32. }
  33. line = trim(line)
  34. if width == 0 {
  35. width = len(line)
  36. }
  37. if len(line) != width {
  38. log.Fatalf("Expected input line to be of length %d but has length %d", width, len(line))
  39. }
  40. height++
  41. plat = append(plat, line...)
  42. }
  43. // Find regions
  44. regions := make([][]int, 1, 32)
  45. degree := make([]int, len(plat))
  46. incidence := make([]int, len(plat))
  47. for i, plot := range plat {
  48. up, lt, rt, dn := i-width, i-1, i+1, i+width
  49. if dn < len(plat) && plot == plat[dn] {
  50. degree[i]++
  51. }
  52. if i/width == rt/width && plot == plat[rt] {
  53. degree[i]++
  54. }
  55. if 0 <= up && plot == plat[up] {
  56. degree[i]++
  57. regionId := incidence[up]
  58. incidence[i] = regionId
  59. regions[regionId] = append(regions[regionId], i)
  60. }
  61. if 0 <= lt && i/width == lt/width && plot == plat[lt] {
  62. degree[i]++
  63. regionId := incidence[lt]
  64. if incidence[i] == 0 {
  65. incidence[i] = regionId
  66. regions[regionId] = append(regions[regionId], i)
  67. continue
  68. }
  69. if incidence[i] == regionId {
  70. continue
  71. }
  72. // If we made it this far, then incidence[i] was just
  73. // set to incidence[plat[up]]. Therefore, plat[up]
  74. // exists.
  75. // Join subregions with different ids.
  76. var src, tgt int
  77. switch {
  78. case regionId < incidence[i]:
  79. tgt, src = regionId, incidence[i]
  80. case regionId > incidence[i]:
  81. src, tgt = regionId, incidence[i]
  82. }
  83. for _, pid := range regions[src] {
  84. incidence[pid] = tgt
  85. regions[tgt] = append(regions[tgt], pid)
  86. }
  87. regions = slices.Delete(regions, src, src+1)
  88. incidence[i] = tgt
  89. continue
  90. }
  91. if incidence[i] > 0 {
  92. continue
  93. }
  94. region := make([]int, 1, 32)
  95. region[0] = i
  96. regions = append(regions, region)
  97. incidence[i] = len(regions) - 1
  98. }
  99. regions = regions[1:]
  100. // Find prices
  101. total := 0
  102. for _, r := range regions {
  103. perimeter := 0
  104. for _, pid := range r {
  105. perimeter += 4 - degree[pid]
  106. }
  107. total += len(r) * perimeter
  108. }
  109. fmt.Printf("%d\n", total)
  110. }