思路:
通过审题,测试一组数据可以知道每次需要除去的数据是 每个数字 从左到右最后一次出现的坐标,且这个坐标在最左边(贪心)
O(N^2)作法,被TLE的做法:
#include <bits/stdc++.h>
using namespace std;
unordered_map<int, int> Hash;
const int N = 2e5 + 10;
struct A
{
int appear;
int number;
} number[N];
void solve()
{
int n, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
int a;
cin >> a;
number[i].number = a;
number[i].appear = ++Hash[a];
}
int m = n;
while (1)
{
for (int i = 1; i <= m; i++)
{
int N = number[i].number, A = number[i].appear;
if (Hash[N] == A)
{
for (int j = i; j <= m; j++)
{
int N = number[j].number;
Hash[N]--;
}
ans++;
m = i - 1;
break;
}
}
if (m == 0)
break;
}
cout << ans << endl;
}
用Hash表存储每个数字出现的次数,然后从头开始遍历,第一次满足: 该数在该点的出现次数=该数的出现次数,则满足去除的条件,然后从该点开始 在除去该点后面的坐标,继续更新。
TLE的原因:
因为每次去除掉一个点后,删除点的时候没有多余操作,因此都需要从头开始再遍历,时间复杂度为N^2,没利用删除点的信息。
O(N)做法:
用一个vector<int> number[N] 储存 每个数字出现的坐标,然后找到标号最小的第一次出现的坐标,将该坐标及其之后的坐标全部删除,删除的时候找到每个被删元素的 前一个元素的坐标,把这些坐标取最小值,那么就可以得到第二次坐标最小值(依次迭代)
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
vector<int> arr[N];
int A[N];
void solve()
{
int n, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
int a;
cin >> a;
A[i] = a;
arr[a].push_back(i);
}
int Min = N;
for (int i = 1; i <= n; i++)
{
if (arr[i].size())
Min = min(Min, arr[i].back()); // 找到要删除的点的下标
}
int i = n; // 数字长度
while (i >= 1)
{
int temp = Min;
for (int j = temp; j <= i; j++)
{
arr[A[j]].pop_back();
if (arr[A[j]].size())
Min = min(Min, arr[A[j]].back()); // 删除后 再拿该点删除前的那个点
}
ans++;
i = temp - 1;
}
cout << ans << endl;
}
// 解题的关键是找到每个数 最后一次出现 且在最左边的数据 那么我需要一个数据结构 存储每个数的出现次数,序号(最左边),
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}