• UVa 12174


    链接:

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3326

    题意:

    你正在使用的音乐播放器有一个所谓的乱序功能,即随机打乱歌曲的播放顺序。
    假设一共有s首歌,则一开始会给这s首歌随机排序,全部播放完毕后再重新随机排序、继续播放,依此类推。
    注意,当s首歌播放完毕之前不会重新排序。这样,播放记录里的每s首歌都是1~s的一个排列。
    给出一个长度为n(1≤s,n≤100000)的播放记录(不一定是从最开始记录的),你的任务是统计下次随机排序所发生的时间有多少种可能性。
    例如,s=4,播放记录是3, 4, 4, 1, 3, 2, 1, 2, 3, 4,不难发现只有一种可能性:前两首是一个段的最后两首歌,后面是两个完整的段,因此答案是1;
    当s=3时,播放记录1, 2, 1有两种可能:第一首是一个段,后两首是另一段;前两首是一段,最后一首是另一段。答案为2。

    分析1:

    使用滑动窗口,窗口大小是“基本”固定的(因为还需要考虑不完整的段),因此只需要一个指针;
    而且所有数都是1~s的整数,只需要一个数组即可保存每个数在窗口中出现的次数。
    再用一个变量记录在窗口中恰好出现一次的数的个数,则可以在O(n)时间内判断出每个窗口是否满足要求(每个整数最多出现一次)。
    这样,就可以枚举所有可能的答案,判断它对应的所有窗口,当且仅当所有窗口均满足要求时这个答案是可行的。
    可以把所有窗口都映射成窗口0 ~ s-1,使用求模即可。

    代码1:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 
     6 const int UP = 100000 + 5;
     7 
     8 int a[UP], num[UP]; //num[x]记录当前窗口中x值的个数
     9 bool can[UP]; //can[i]表示以位置i结尾的一个序列是否可能是1~s的一个排列
    10 
    11 int main(){
    12     int T;
    13     scanf("%d", &T);
    14     while(T--){
    15         int s, n;
    16         scanf("%d%d", &s, &n);
    17         for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    18 
    19         memset(num, 0, sizeof(num));
    20         memset(can, true, sizeof(can));
    21 
    22         int tot = 0; //tot记录当前窗口中只出现一次的数的个数
    23         for(int i = 0; i < n + s; i++){
    24             if(i < n && ++num[a[i]] == 1) tot++;
    25             if(i >= s && --num[a[i-s]] == 0) tot--;
    26             if(min(i, n-1) - max(i-s, -1) != tot) can[i%s] = false;
    27         }
    28 
    29         int ans = 0;
    30         for(int i = 0; i < s; i++) if(can[i]) ans++;
    31         printf("%d
    ", ans);
    32     }
    33     return 0;
    34 }

    分析2:

    这个比较直观。对于1 2 1这样的播放列表,两个1之间必然存在一个窗口的交界位置。类似地,对于同一个数字的两次相邻的出现,
    都能排除一些不可能的位置,当把所有不可能的位置排除后,剩下的位置个数便是答案。
    可以把所有位置都映射成位置0 ~ s-1,使用求模即可。

    代码2:

     1 #include <cstdio>
     2 #include <cstring>
     3 
     4 const int UP = 100000 + 5;
     5 
     6 int a[UP], f[UP]; //f[a[i]] 记录与 a[i] 的值相同的前一个位置
     7 bool can[UP]; //can[i]表示以位置i结尾的一个序列是否可能是1~s的一个排列
     8 
     9 int main(){
    10     int T;
    11     scanf("%d", &T);
    12     while(T--){
    13         int s, n;
    14         scanf("%d%d", &s, &n);
    15         for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    16 
    17         memset(f, -1, sizeof(f));
    18         memset(can, true, sizeof(can));
    19 
    20         for(int x, i = 0; i < n; f[x] = i++){
    21             x = a[i];
    22             if(f[x] != -1){ //判断x之前是否出现过
    23                 if(i - f[x] >= s) continue; //相邻的两个x的距离不小于s,则位置f[x]~i-1都可能是结尾
    24                 int L = f[x] % s, R = i % s; //把位置L和R都映射成位置0 ~ s-1
    25                 if(L < R){
    26                     for(int t = 0; t < L; t++) can[t] = false;
    27                     for(int t = R; t < s; t++) can[t] = false;
    28                 }
    29                 else for(int t = R; t < L; t++) can[t] = false;
    30             }
    31         }
    32 
    33         int ans = 0;
    34         for(int i = 0; i < s; i++) if(can[i]) ans++;
    35         printf("%d
    ", ans);
    36     }
    37     return 0;
    38 }

    分析3:

    这是我自己想到的做法,跟分析2基本相同,只是没有使用求模来映射位置,速度要慢一点。在这里也把代码贴一下吧。

    代码3:

     1 #include <cstdio>
     2 #include <cstring>
     3 
     4 const int UP = 100000 + 5;
     5 
     6 int a[UP], f[UP]; //f[a[i]] 记录与 a[i] 的值相同的前一个位置
     7 bool no[UP*2]; //no[i]表示以位置i结尾的一个序列是否可能是1~s的一个排列
     8 
     9 int main(){
    10     int T;
    11     scanf("%d", &T);
    12     while(T--){
    13         int s, n;
    14         scanf("%d%d", &s, &n);
    15         for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    16 
    17         memset(f, 0, sizeof(f));
    18         memset(no, false, sizeof(no));
    19 
    20         for(int x, i = 1; i <= n; f[x] = i++){
    21             x = a[i];
    22             if(f[x]){ //判断x之前是否出现过
    23                 if(i - f[x] >= s) continue; //相邻的两个x的距离不小于s,则位置f[x]~i-1都可能是结尾
    24                 for(int t = i; t - f[x] < s; t++) no[t] = true; //排除不可能是结尾的位置
    25                 for(int t = f[x] - 1; t && i - t <= s; t--) no[t] = true;
    26             }
    27         }
    28 
    29         int ans = 0;
    30         for(int t, i = 1; i <= s; i++){
    31             for(t = i; t <= n + s; t += s) if(no[t]) break; //判断以位置i为结尾是否可行
    32             if(t > n + s) ans++;
    33         }
    34         printf("%d
    ", ans);
    35     }
    36     return 0;
    37 }
  • 相关阅读:
    OpenCV-Python图形图像处理:利用黑帽去除图像浅色水印
    单片机实验四:定时器控制发光二极管的亮灭+简单输出连续矩形脉冲
    实验5 linux网络编程
    第十六届全国大学智能汽车竞赛竞速比赛规则-讨论稿
    写给自己的TypeScript 入门小纲
    写给自己的TypeScript 入门小纲
    Node.js自学笔记之回调函数
    Node.js自学笔记之回调函数
    来简书坚持一个月日更之后
    全选或者单选checkbox的值动态添加到div
  • 原文地址:https://www.cnblogs.com/hkxy125/p/8152287.html
Copyright © 2020-2023  润新知