CF1654E Arithmetic Operations 题解
更好的阅读体验
题目让我们求改变数字的最少次数,那我们转化一下,
求可以保留最多的数字个数
我们先考虑两种暴力方法。
第一种暴力方法:
大体思路:因为要保留的最多,那么我们肯定要在众多等差数列中找能对应数字最多的那一个并保留下来。
首先,我们要知道一个概念。
对于这道题,那么我们可以暴力枚举公差
对于同一个公差
如果有
那我们可以想到,用桶记录计算出来的值
如果
这种方法的时间复杂度为
第二种暴力方法:
考虑动态规划,设
我们可以枚举上一个数字
那这个序列的公差是多少呢?
这样考虑,中间有
如果除不尽怎么办呢,那么这就说明
那
这个等会儿讲。
那么为了平衡这两种暴力算法,我们可以这样办:
取输入的数列
我们只使用第一种方法枚举
我们使用第二种方法枚举
下面探讨第二种方法的时间复杂度,
首先回归到前面的问题,来探讨
到哪里好解决,就是
而开始的地方,是
首先,因为公差
首先假设
所以
所以,第二种方法的时间复杂度为
那么这个题的时间复杂度就为
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map>
using namespace std;
const int N = 100010;
int n;
int a[N], maxx, sqrtmaxx;
int u[(int)(N + N * sqrt(N))]; // 第一种暴力方法的桶
unordered_map<int, int> f[N]; // 第二种暴力方法的动态规划数组。
int max_keep() {
int ans = 0;
for (int d = 0; d <= sqrtmaxx; d++) { // 第一种暴力方法,枚举公差 D
for (int i = 1; i <= n; i++) {
ans = max(ans, ++u[a[i] + (n - i) * d]);
}
for (int i = 1; i <= n; i++) {
u[a[i] + (n - i) * d]--;
}
}
for (int i = 1; i <= n; i++) { // 第二种暴力方法,动态规划
for (int j = max(1, i - sqrtmaxx); j < i; j++) {// j只用从 i - sqrt(m) 开始枚举
if ((a[i] - a[j]) % (i - j) == 0) {
int x = (a[i] - a[j]) / (i - j);
if (x <= sqrtmaxx) continue;
f[i][x] = max(f[i][x], f[j][x] + 1);
ans = max(f[i][x] + 1, ans);
}
}
}
for (int i = 1; i <= n; i++) f[i].clear(); // 清空数组
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], maxx = max(maxx, a[i]);
sqrtmaxx = sqrt(maxx);
int ans1 = 0, ans2 = 0;
ans1 = max_keep();
reverse(a + 1, a + n + 1); // 应对公差为负数的情况
ans2 = max_keep();
cout << n - max(ans1, ans2) << '\n';
return 0;
}