算法学习记录:[NOIP2010]机器翻译
题目链接
https://ac.nowcoder.com/acm/contest/20960/1003
记录:
这道题我真的吃💩,我脑子抽了,用滑窗去做,然后下标换算被自己弄的特别复杂(下标套数组再套一层st数组,方便直接查),
把自己绕晕了,555(还没找出问题在哪,但应该是下标的问题,本来应该踢出去的数字还在里面)。
最后队列直接搞定了,以后记住如果下标太乱,先写带数据结构的再手动模拟数据结构。
AC代码(手写队列)
#include <iostream>
using namespace std;
const int M = 10000;
int n, m, ans;
int hh, tt = -1;
bool st[M];
int q[M];
int main()
{
cin >> m >> n;
for (int i = 0; i < n; ++ i)
{
int x; cin >> x;
if (!st[x])
{
if (tt - hh + 1 == m)
st[q[hh ++ ]] = false;
st[x] = true;
q[ ++ tt] = x;
ans ++ ;
}
}
cout << ans;
}
AC代码(STL队列)
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int M = 10000;
int n, m, ans;
bool st[M];
queue<int> q;
int main()
{
cin >> m >> n;
for (int i = 0; i < n; ++ i)
{
int x; cin >> x;
if (!st[x])
{
if (q.size() == m)
{
st[q.front()] = false;
q.pop();
}
st[x] = true;
q.push(x);
ans ++ ;
}
}
cout << ans;
}
AC代码(暴力)
自己写的暴力版并不好,因为我们的操作都是拿一个放入一个,所以内存在某个时间点满了以后,内存就一直是满的。
所以并不需要枚举 sz,直接让 m -- 至 0 就可以代表内存已满。
也不需要用 cnt 来表示内存所有数字的编号,因为最大的编号再大也不可能超过 i,所以我们可以用 i 来代表 a 数组中的编号,进行枚举。
利用好,可以用的信息。但是在意识不到如何得到自己想要的信息的时候,添一个变量\数组也是比较舒服方法。
#include <iostream>
#include <cstring>
using namespace std;
// 思路:内存中的用st进行标记,暴力枚举内存每次踢掉内存中最早的
const int N = 1005;
const int MAXN = 0x3f3f3f3f;
int n, m, cnt, ans;
bool st[N];
int a[N];
int main()
{
cin >> m >> n;
memset(a, 0x3f, sizeof a);
for (int i = 0; i < n; ++ i)
{
int x; cin >> x;
int sz = 0;
if (st[x]) continue;
// st[x] = false, 才需要找到最早的踢掉
for (int j = 0; j <= N; ++ j)
if (st[j]) ++ sz;
if (sz == m)
{
int mi = MAXN, mindex = MAXN;
for (int j = 0; j <= N; ++ j)
if (st[j])
if (mi > a[j])
{
mi = a[j];
mindex = j;
}
st[mindex] = false;
}
ans ++ , st[x] = true, a[x] = cnt ++ ;
}
cout << ans;
return 0;
}
AC代码(针对暴力的优化)
注意不要 a[i] = x ,这样得到数组不是连续的,会导致队头下标的计算错误。
#include <iostream>
#include <cstring>
using namespace std;
// 因为靠前的元素一定是已经丢出去的,所以我们只需要判断末尾的m个元素即可
// 将值存入a[],判断末尾的m个元素
const int MAXN = 1005;
int n, m, tt, ans;
int st[MAXN], a[MAXN];
int main()
{
cin >> m >> n;
memset(a, -1, sizeof a);
for (int i = 0; i < n; ++ i)
{
int x; cin >> x;
// 先判断存在,再加入
bool flag = false;
for (int j = i; j >= tt - m + 1; -- j)
{
if (j < 0) break;
if (a[j] == x) // 存在
flag = true;
}
if (!flag)
{
a[ ++ tt] = x;
ans ++ ;
}
}
cout << ans;
return 0;
}
AC代码(二重优化,队列的雏形出来啦)
#include <iostream>
using namespace std;
// 内层循环枚举的目的就是为了找出是否出现过的数字,
// st[] 照样可以找到出现过的数字,O(1) 优于 O(N),所以并不需要一一枚举。
// 内存的范围已经确定了,内存中的数字也已经确定了,
// 优化的条件也就具备了。
const int N = 1005;
int n, m, ans, tt = -1;
int st[N], a[N];
int main()
{
cin >> m >> n;
for (int i = 0; i < n; ++ i)
{
int x; cin >> x;
if (st[x]) continue; // 过滤掉内存中的值
a[ ++ tt] = x;
st[x] = true;
ans ++ ;
// ps:最开始的情况,如果窗口在1的位置,那么必然0是窗口前的位置
// 所以严格 > 0。
// tt-m+1,是队头的位置,那么前面的位置自然减去 1。
if (tt - m + 1 > 0) st[a[tt - m + 1 - 1]] = false;
}
cout << ans;
return 0;
}
本文来自博客园,作者:想个昵称好难ABCD,转载请注明原文链接:https://www.cnblogs.com/ClockParadox43/p/17414887.html