[滑动窗口] UVa12174 Shuffle的播放记录(滑窗做预处理)(较复杂区间处理)
程序员文章站
2022-03-11 19:13:52
...
题目
思路
1.暴力:枚举一个播放记录的起点,然后进行是否重复的判断。
2.预处理+暴力枚举:先用滑窗滑动整个序列,判断出每个地方能否作为一个播放记录的开头。(即在接下来的s长度的序列里无重复)最后枚举时,依次判断此种情况的每个窗口是否都能作为开头即可。
3.需要注意本题特殊情况比较多,所以含有很多区间操作。拿题目所给样例7 3 5 7 3来说。由于s>n,所以就要把整个序列补成这样的:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
数据 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 5 | 7 | 3 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
即前后各加s个-1。
这时候就可以理解
(1).对于一个播放记录的起点,从0到n+s(10)。
(2).当窗口起点小于n时,只要满足tot == i即可以构成合法播放记录。
(3).当窗口起点大于n时,只要满足tot == n+s-i即可以构成合法播放记录。
(4).窗口向右移时,删除的最左边的那个元素,当其为-1或失去后cnt不等于0,就不减tot;
(5).窗口向右移时,新增的最右边的那个元素,当其为-1或cnt加1钱不等于0,就不加tot;
代码
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 1000;
int s, n, x[maxn*3], cnt[maxn], ok[maxn*2];
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &s, &n);
// 向序列的左右两边,分别加-1.这样就避免了负下标和越界的问题。
fill(x, x + n + 2 * s, -1);
for (int i = 0; i < n; i++) scanf("%d", &x[i + s]);
int tot = 0; // 窗口中只出现一次的元素的个数
fill(cnt + 1, cnt + s + 1, 0); // cnt是窗口中某个元素出现的次数
fill(ok, ok + n + s + 1, 0); // ok[i]=1表示以i开头的滑动窗口,没有重复元素
// 计算ok数组
for (int i = 0; i < n + s + 1; i++) {
if (tot == s) ok[i] = 1;
if (i < s && tot == i) ok[i] = 1; // 左边越界
if (i > n && tot == n + s - i) ok[i] = 1; // 右边越界
if (i == n + s) break;
if (x[i] != -1 && --cnt[x[i]] == 0) tot--;
if (x[i + s] != -1 && cnt[x[i + s]]++ == 0) tot++;
}
// 枚举每种答案
int ans = 0;
for (int i = 0; i < s; i++) {
int vaild = 1;
for (int j = i; j < n + s + 1; j += s)
if (!ok[j]) vaild = 0;
if (vaild) ans++;
}
if (ans == n + 1) ans = s; //特殊情况
printf("%d\n", ans);
}
return 0;
}
上一篇: JSP