LC 2967. 使数组成为等数数组的最小代价
https://leetcode.cn/problems/minimum-cost-to-make-array-equalindromic/
大概可以分解成为3个部分:
- 我们将数组排序的话,最优解肯定是在每个数的附近,但是我们怎么知道这些数的附近的“回文数”呢?
- 题目有个限制就是回文数的最大值是 10^9. 如果枚举前缀的话,这个量级差不多就是 10^5 (大概枚举出来是10999).
- 然后我们遍历回文数,期间会穿过 `nums`. 可以通过记录中间的状态变化,达到 `O(N)` 的时间复杂度。
题目还有两个优化:
- 回文数不用每次都计算。这个计算好像放在class init里面不行,还必须写在全局代码初始化里面。
- 可以根据 `nums` 最大最小值选择回文数,这个过程可以将许多回文数删除掉。
代码稍微有点乱,在 `cap_options` 里面还用了二分,不用二分时间会上来许多。
#!/usr/bin/env python
# coding:utf-8
# Copyright (C) dirlt
from typing import List
def gen_options(N):
def reverse(x):
ans = 0
while x:
ans = ans * 10 + x % 10
x = x // 10
return ans
for x in range(10):
yield x
for sz in range(2, N):
mid = sz // 2
lower, upper = 10 ** (mid - 1), 10 ** mid
if sz % 2 == 1:
for head in range(lower, upper):
tail = reverse(head)
for x in range(10):
yield (head * 10 + x) * upper + tail
else:
for head in range(lower, upper):
tail = reverse(head)
yield head * upper + tail
OPTIONS = list(gen_options(10))
class Solution:
def minimumCost(self, nums: List[int]) -> int:
nums.sort()
options = OPTIONS
def cap_options():
s, e = 0, len(options) - 1
while s <= e:
m = (s + e) // 2
if options[m] > nums[0]:
e = m - 1
else:
s = m + 1
lower = e
s, e = 0, len(options) - 1
while s <= e:
m = (s + e) // 2
if options[m] < nums[-1]:
s = m + 1
else:
e = m - 1
upper = s
return options[lower: upper + 1]
capped_options = cap_options()
acc = ans = sum(nums)
left, last = 0, 0
i = 0
for x in capped_options:
a, b = 0, 0
acc += left * (x - last)
while i < len(nums) and nums[i] <= x:
left += 1
a += abs(nums[i] - last)
b += abs(nums[i] - x)
i += 1
acc -= a
acc += b
right = len(nums) - left
acc -= right * (x - last)
last = x
ans = min(ans, acc)
# print(x, acc)
return ans