题意 : 给你一本书、整本书有若干个不同的知识点,书的每一页都包含了一个知识点 ( 包含的知识点可能与其他页重复 ),问你如果取连续的几页用来读完书中包含的所有知识点,那么这连续的几页最少是多少?( 书的页数 ≤ 1e6 )
分析 : 最直接的想法就是先 O(n) 地枚举页首,然后计算贡献,在过程中维护最小值即可。但是这样做最差的情况就可能达到 O(n^2) 因此需要优化。那现在来考虑一下如果当前枚举到 st 且符合条件的页尾为 en ,那么其贡献就是 en - st + 1,接下来枚举 st+1 的时候会发现其符合条件的页尾 (令为 en') 必定不小于 en,即 en' ≥ en。道理应该不难理解,因为如果当前从 st 到 st+1 则自然少了 st 这一页包含的知识点,而 st 这一页作为页首符合条件的页尾都要达到 en ,那么少了一个知识点的 st+1 作为页首作为符合条件的相应页尾必然不小于 en ,因此此题使用尺取法可行!具体实现就看代码吧!

#include<stdio.h> #include<map> #include<set> using namespace std; const int maxn = 1e6 + 10; int Page[maxn], num[maxn]; set<int> Have; map<int, int> mp; int tot; int main(void) { int n; while(~scanf("%d", &n)){ mp.clear(); tot = 1; for(int i=1; i<=n; i++){ num[i] = 0; scanf("%d", &Page[i]); if(!mp.count(Page[i])) mp[Page[i]] = tot++; } int L, R, ans; L = R = 1; ans = n; Have.clear(); Have.insert(mp[Page[L]]); num[mp[Page[L]]]++; while(L <= R && R <= n){ if((int)Have.size() == tot-1){ ans = min(ans, R-L+1); if(--num[mp[Page[L]]] == 0) Have.erase(mp[Page[L]]); L++; }else{ R++; Have.insert(mp[Page[R]]); num[mp[Page[R]]]++; } } printf("%d ", ans); } return 0; }