第 376 场力扣周赛

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

题目

输入长度为 \(n\) 的整数数组 \(a\),输出执行任意次操作后使得数组中的数全部相等并且是回文数的最小代价(要求该回文数小于 \(10^{9}\))。每次操作可以将数组中的某个数(假设为 \(a_{i}\))修改为任意正整数(假设为 \(x\)),对应的代价为 \(|a_{i}-x|\)。

数据范围:\(1\leq n\leq 10^{5}\),\(1\leq a_{i}\leq10^{9}\)。

思路

如果没有限制是回文数,那么将数组排序之后按如下方式修改,代价是最小的(假设有序数组中的元素为 \(a_{0},a_{1},\dots,a_{n-1}\)):

  • 当 \(n\) 为奇数时,将所有数修改为中位数 \(a_{\lfloor\frac{n}{2}\rfloor}\)。
  • 当 \(n\) 为偶数时,将所有数修改为区间 \([a_{\lfloor\frac{n-1}{2}\rfloor},a_{\lfloor\frac{n}{2}\rfloor}]\) 内的某个数。

首先使用反证法证明,最小代价可以通过将所有数修改为数组中的某个数取到。

  • 假设将所有数修改为 \(x\),\(x\) 在区间 \((a_{i},a_{i+1})\) 范围内,其中 \(a_{i}\) 和 \(a_{i+1}\) 表示数组中相邻的两个数。此时 \(x\) 左边有 \(i+1\) 个元素,右边有 \(n-i\) 个元素。将 \(x\) 修改为 \(a_{i+1}\) 会使代价增加 \((i+1)\times (a_{i+1}-x)\),并且使代价减少 \((n-i)\times(a_{i+1}-x)\)。将 \(x\) 修改为 \(a_{i}\) 会使代价减少 \((i+1)\times (x-a_{i})\),并且使代价增加 \((n-i)\times(x-a_{i})\)。
  • 当 \(i+1<n-i\) 时,将 \(x\) 修改为 \(a_{i+1}\) 会使代价减少;当 \(i+1>n-i\) 时,将 \(x\) 修改为 \(a_{i}\) 会使代价减少;当 \(i+1=n-i\) 时,将 \(x\) 修改为 \(a_{i}\) 或者 \(a_{i+1}\) 代价不变。
  • 特别的,\(x\) 在区间 \([1,a_{0})\) 或者 \((a_{n-1},+\infty]\) 范围内时,同理。

然后再使用反证法证明上述结论:

  • 假设将数组中的数都修改为 \(a_{i}\) 时,代价最小,\(a_{i}\) 不满足上述条件。
  • 当 \(n\) 为奇数时,由于 \(a_{i}\) 不是中位数:
    • 当 \(i<\lfloor\frac{n}{2}\rfloor\) 时,有 \(i+1<n-1-i\),此时 \(i\) 每向中位数移动一位,代价都会减少 \((n-1-i)-(i+1)>0\)。
    • 反之亦然。
  • 当 \(n\) 为偶数时,同理。

综上,得出按照上述方式修改代价最少,即 \(x\) 越靠近中位数代价越小。所以,如果需要将所有数修改为某个回文数,那么该回文数一定是最靠近中位数的回文数。PS:还是灵神的证明更简单。

该题有两种方法可以找到距离 \(x\) 的最近回文数:

  • 方法一:将 \(x\) 的前半部分作为回文根,对称之后得到回文数 \(y\)。如果 \(y<x\),则将回文根加一再做对称得到回文数 \(z\),然后取 \(y\) 和 \(z\) 中距离最近者;如果 \(y>x\),则将回文根减一再做对称得到回文数 \(z\),然后取 \(y\) 和 \(z\) 中距离最近者;否则,\(x\) 本身就是回文数。注意,排除大于等于 \(10^{9}\) 的回文数,以及做加减法时可能会遇到回文根为 \(100\dots0\) 或 \(99\dots9\) 的特殊情况,此时做对称会得到错误答案,我们应该直接根据长度构造回文数。(这个代码还挺好看,把所有情况都直接循环枚举,就可以不用写那么多判断语句)
  • 方法二:枚举出 \([1,10^{9}]\) 范围内的所有回文数,然后二分找到距离 \(x\) 最近的回文数。

执行操作使频率分数最大

题目

输入长度为 \(n\) 的整数数组 \(a\) 和一个整数 \(k\),输出经过至多 \(k\) 次操作之后,数组中众数的最大频率。每次操作可以选择数组中的某个数 \(a_{i}\),将其增加或者减少 \(1\)。

数据范围:\(1\leq n\leq 10^{5}\),\(1\leq a_{i}\leq 10^{9}\),\(0\leq k\leq 10^{14}\)。

思路

排序 + 前缀和 + 滑动窗口。要使众数尽可能多,可以首先对数组排序,假设最终众数有 \(x\) 个,那么这 \(x\) 个数一定是在某个子数组中。使用滑动窗口,将尽可能多的数包含在窗口内,同时满足操作次数小于等于 \(k\),如果大于 \(k\) 则将左端点右移。类似上一题,将窗口内的所有数都修改为窗口的中位数,所需的操作次数最少。窗口的操作次数可以使用前缀和 \(O(1)\) 的计算出来,假设窗口的左右端点的下标分别为 \(i\) 和 \(j\),中位数的下标为 \(k\),则窗口的操作次数为:\((a_{k}\times (k-i)-(s[k]-s[i]))+((s[j+1]-s[k+1])-a_{k}\times (j-k))\)。

第 375 场力扣周赛

统计最大元素出现至少 K 次的子数组

题目

输入长度为 \(n\) 的数组 \(a\) 和整数 \(k\),输出满足 \(\max(a)\) 至少出现 \(k\) 次的子数组的数目。

数据范围:\(1\leq n\leq 10^{5}\),\(1\leq k\leq 10^{5}\)。

思路

首先计算出最大值,然后将所有最大值的下标放入列表 \(l\) 中,最后枚举右端点即可。假设列表的长度为 \(m\),当前枚举到 \(i\),当 \(i<m-1\) 时,区间 \([l[i],l[i+1]-1]\) 范围内的右端点都对应 \(l[i-k+1]+1\) 数量的左端点,将它们相乘加入答案。特别的,当 \(i=m-1\) 时,取区间 \([l[i],n-1]\)。PS:也可以滑动窗口,使窗口内只包含 \(k-1\) 个最大值,这样计算答案的空间复杂度为 \(O(1)\)。

统计好分割方案的数目

题目

输入长度为 \(n\) 的数组 \(a\),输出将数组分割为若干不相交子数组的方案数。不相交表示子数组之间没有相同的元素,答案对 \(10^{9}+7\) 取余。

数据范围:\(1\leq n\leq 10^{5}\)。

思路

因为要求子数组之间没有相同元素,那么相同元素必定只会出现在一个子数组中,首先统计每个元素的最小和最大下标,这两个下标构成的区间是不可分割的。然后将所有不可分割的区间进行合并,最后剩余的区间数假设为 \(m\),那么就会有 \(2^{m-1}\) 种分割方案(因为 \(m\) 个区间有 \(m-1\) 个分割位置,每个分割位置有分割或者不分割两种状态)。PS:① 可以边计数边做乘法,不使用快速幂;② 可以只统计最大下标,然后遍历数组时维护最大下标的最大值,如果当前下标等于该值,那么就可以做一次分割。(代码

第 119 场力扣夜喵双周赛

消除相邻近似相等字符

题目

输入长度为 \(n\) 的字符串 \(s\),输出所需的最少操作次数,使得字符串 \(s\) 中的相邻字符在字母表中的距离大于 \(1\)。每次操作可以将字符串 \(s\) 中的某个字符修改为任意字符。

数据范围:\(1\leq n\leq 100\)。

思路

思路一:距离大于 \(1\) 的相邻字符可以将字符串 \(s\) 分割为若干子串,每个子串所需的最少操作次数为 \(\lfloor\frac{l}{2}\rfloor\),其中 \(l\) 表示子串的长度。

思路二:如果相邻字符距离小于等于 \(1\),那么贪心的修改右边的字符即可。

两种思路原理是一样的,只是实现时略有不同。

关闭分部的可行集合数目

题目

输入整数 \(n\) 表示有 \(n\) 个节点,长度为 \(m\) 表示无向边的数组 \(e\)(包含重边),以及整数 \(d\)。输出删除节点的方案数,使得剩余节点两两之间的最短路不超过 \(d\)。

数据范围:\(1\leq n\leq 10\),\(0\leq m\leq 1000\)。其他数据不会影响时间复杂度,所以不列出。

思路

题目要求满足条件的方案数,首先想到枚举所有方案,总共有 \(2^{n}\) 个方案,然后对每个方案求删除节点后的多源最短路(Floyd 算法),如果剩余节点两两之间的最短路都不超过 \(d\),那么答案就加一,总时间复杂度为 \(O(m+2^{n}n^{3})\)。因为是求最短路,所以重边可以只保留最小的那条边。