0%

2967. 使数组成为等数数组的最小代价

2967.使数组成为等数数组的最小代价

题目描述:

给你一个长度为 n 下标从 0 开始的整数数组 nums

你可以对 nums 执行特殊操作 任意次 (也可以 0 次)。每一次特殊操作中,你需要 按顺序 执行以下步骤:

  • 从范围 [0, n - 1] 里选择一个下标 i 和一个 整数 x
  • |nums[i] - x| 添加到总代价里。
  • nums[i] 变为 x

如果一个正整数正着读和反着读都相同,那么我们称这个数是 回文数 。比方说,121255265756 都是回文数,但是 2446235 都不是回文数。

如果一个数组中的所有元素都等于一个整数 y ,且 y 是一个小于 109回文数 ,那么我们称这个数组是一个 等数数组

请你返回一个整数,表示执行任意次特殊操作后使 nums 成为 等数数组最小 总代价。

数据范围:

$1\le n \le 10^5$

题解:

假设最后全是 $x$ ,则就是假设所有的元素为 $x_i \le x \lt x_j$ 。也就是数组中左侧的元素全为 $x_i$ ,右侧的全为 $x_j$ , $x$ 是一个分界线。

总代价就是 $\sum x - x_i + \sum x_j - x$ 。跟上面那个题目类似,当 $x$ 等于中位数时有最小值,但是增加了回文数限制,因此可以找中位数两侧最近的回文数,然后取最小值即可。

需要找一个数左右两侧的回文数,这个不是很好处理。

可以先预处理出所有的在 $[1,1e9]$ 范围内的回文数,可以先枚举左半边,然后翻转左半边,然后拼接得到。

而且需要写成预处理代码只会运行一遍的那种,减少运行时间。

先找中位数左侧最接近的回文数,比如找小于中位数的,可以使用lower_bound 找出来大于等于的,然后再减一。找小于等于的可以使用 upper_bound 然后减一。

再去找中位数右侧最接近的回文数,比如找大于等于的,可以使用lower_bound找出来大于等于的。找大于的可以直接 upper_bound

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
vector<int> a;
auto init = []()
{
for (int i = 0; i <= 10000; ++i)
{
// i 是左侧数字
// 偶数
long long num = i;
int tmp = i;
while (tmp)
{
num = num * 10 + tmp % 10;
tmp /= 10;
}
if (num >= 1 && num <= 1000000000)
a.emplace_back(num);
// 奇数
for (int j = 0; j < 10; ++j)
{
num = i * 10 + j;
tmp = i;
while (tmp)
{
num = num * 10 + tmp % 10;
tmp /= 10;
}
if (num >= 1 && num <= 1000000000)
a.emplace_back(num);
}
}
sort(a.begin(), a.end());
cout << a.back() << endl;
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
long long cost(vector<int> &nums, int x)
{
long long ret = 0;
for (auto &y : nums)
ret += abs(y - x);
return ret;
}
long long minimumCost(vector<int> &nums)
{
int n = nums.size();
sort(nums.begin(), nums.end());
int x = nums[n / 2];
long long sum = 0;
for (auto &x : nums)
sum += x;
long long ans = LLONG_MAX;
// 找左侧
int index = lower_bound(a.begin(), a.end(), x) - 1 - a.begin();
if (index > 0)
ans = min(ans, cost(nums, a[index]));
// 找右侧
index = lower_bound(a.begin(), a.end(), x) - a.begin();
if (index != a.size())
ans = min(ans, cost(nums, a[index]));
return ans;
}
};