22_cluster.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/env python3
  2. import sys
  3. import re
  4. from collections import namedtuple
  5. from itertools import combinations
  6. from heapq import heappop, heappush
  7. Node = namedtuple('Node', 'x, y, size, used, avail')
  8. def parse(f):
  9. for line in f:
  10. if line.startswith('/'):
  11. x, y, size, used, avail, usep = map(int, re.findall(r'\d+', line))
  12. assert used + avail == size
  13. yield Node(x, y, size, used, avail)
  14. def viable_pairs(nodes):
  15. for a, b in combinations(nodes, 2):
  16. if 0 < a.used <= b.avail:
  17. yield a, b
  18. elif 0 < b.used <= a.avail:
  19. yield b, a
  20. def move_steps(nodes):
  21. w = max(node.x for node in nodes) + 1
  22. h = len(nodes) // w
  23. used = [0] * len(nodes)
  24. size = [0] * len(nodes)
  25. for node in nodes:
  26. i = node.y * w + node.x
  27. used[i] = node.used
  28. size[i] = node.size
  29. def neighbors(i):
  30. y, x = divmod(i, w)
  31. if x > 0:
  32. yield i - 1
  33. if x < w - 1:
  34. yield i + 1
  35. if y > 0:
  36. yield i - w
  37. if y < h - 1:
  38. yield i + w
  39. def shortest_path(source, dest):
  40. assert used[source] == 0
  41. frontier = [(0, source)]
  42. explored = {source}
  43. prev = {}
  44. while frontier:
  45. dist, u = heappop(frontier)
  46. if u == dest:
  47. path = []
  48. while u != source:
  49. path.append(u)
  50. u = prev[u]
  51. path.append(source)
  52. return path[::-1]
  53. for v in neighbors(u):
  54. if v not in explored and v != data and used[v] <= size[u]:
  55. heappush(frontier, (dist + 1, v))
  56. explored.add(v)
  57. prev[v] = u
  58. def move(src, dst):
  59. assert dst in neighbors(src)
  60. assert size[dst] >= used[dst] + used[src]
  61. used[dst] += used[src]
  62. used[src] = 0
  63. empties = set(b for a, b in viable_pairs(nodes))
  64. assert len(empties) == 1
  65. empty = empties.pop()
  66. empty = empty.y * w + empty.x
  67. data = w - 1
  68. steps = 0
  69. while data != 0:
  70. path = shortest_path(empty, data - 1)
  71. for i in range(len(path) - 1):
  72. move(path[i + 1], path[i])
  73. empty = path[i + 1]
  74. move(data, empty)
  75. data, empty = empty, data
  76. steps += len(path)
  77. return steps
  78. nodes = list(parse(sys.stdin))
  79. print(sum(1 for pair in viable_pairs(nodes)))
  80. print(move_steps(nodes))