欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[滑动窗口] UVa12174 Shuffle的播放记录(滑窗做预处理)(较复杂区间处理)

程序员文章站 2022-03-11 19:13:52
...

题目

[滑动窗口] UVa12174 Shuffle的播放记录(滑窗做预处理)(较复杂区间处理)

思路

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;
}
相关标签: 滑动窗口