算法学习记录:[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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)