一、前言及题意:
最近一直在找题训练,想要更加系统的补补思维,补补漏洞什么的,以避免被个类似于脑筋急转弯的题目干倒,于是在四处找书,找了红书、蓝书,似乎都有些不尽如人意。这两天看到了日本人的白书,重新读了一遍,其中若干章节写的非常务实也实践起来相当实用,于是这就是白书上面一道推荐的题目,用于训练尺取法的例题。考虑到最近老是读错题,所以就慢慢习惯于首先把个题目翻译成中文之后在进行解读:
杰西卡是个非常可爱的女孩子,因而有若干男孩子追她,最近他的考试要到了,她需要花相当多部分的时间在这件事情上面,吐过他想通过考试,那么她就必须把所有的只是点都熟悉透,但是写教科书的人似乎十分在意一些奇怪的东西,于是他将将有的知道hisIan重复了若干次。杰西卡童鞋希望知道她最少需要看多少页连续的书才能够完成她的复习进度。一个爱慕她的男孩子已经把每一页的知识点使用独立的编号标示了出来,但是他并不能够很好的完成这个统计工作,所以你收到的聘用。
链接:https://vjudge.net/problem/POJ-3320
二、做法:
传统意义上如果我们要枚举连续的片段将会几乎必然花费N2的时间来进行这个枚举,但是根据尺取法的思路,我们并没有必要完全的枚举每种集合(如果他们显然不合理的的话),此时的思路是,首先设置一个游标,使得游标在慢慢往后移动这代表了整个区间的结束位置,没到达一个点,将记录该点在区间中的出现次数,如果之前没有出现过,则应当给CNT变量加上1,如果此时cnt变量等于所有知识点的总数,则应开始移动开始端的游标,每到一处则减少1.。
但是这道题有些坑:
1,及即使关了同步,cin也会因为太慢而超时。
2,因为输入整个只有一行,所以关同步这个操作会使得scanf无法读取到全部所有的数据。
于是注释掉关同步的行,cin换成scanf就可以了。
详见代码:
#include<iostream> #include<stdio.h> #include<math.h> #include<string.h> #include<string> #include<vector> #include<stack> #include<queue> #include<algorithm> #include<set> #include<map> using namespace std; const long long MAXN=1000000+233; int arr[MAXN]; int numb[MAXN]; int cntt[MAXN]; int ans=MAXN; int n; set<int>s; void init() { cin>>n; int pp=0; for(int i=0;i<n;++i) { scanf("%d",&arr[i]); // cin>>arr[i]; if(!s.count(arr[i]))numb[pp++]=arr[i],s.insert(arr[i]); }sort(numb,numb+pp); int spos=-1;int cnt=0; for(int i=0;i<n;++i) { int pos=lower_bound(numb,numb+pp,arr[i])-numb; if(!cntt[pos])cnt++;cntt[pos]++; while(cnt==pp) { ans=min(ans,i-spos); spos++; int pos2=lower_bound(numb,numb+pp,arr[spos])-numb; cntt[pos2]--; if(!cntt[pos2])cnt--; } } cout<<ans<<"\n"; } int main() { // cin.sync_with_stdio(false); init(); return 0; }