http://poj.org/problem?id=3320
题意:为了准备考试,Jessica开始读一本很厚的课本。要想通过考试,必须要课本上每个知识点看一遍。这本书一共有P页,第i页恰好有一个知识点ai(每个知识点都有一个编号),同一个知识点可能被多页反复提到。所以她想阅读连续的一些页把书本中所有的知识点全覆盖到。给定每页写到的知识点,求出阅读的最少页数。
因为所求区间为连续,所以我们使用尺取法。
尺取法过程:记录下每个知识点出现的次数,从区间头部把s取出时,则s对应的知识点出现的次数减1,如果此时某个知识点出现的次数为0时,则将尾部t向后推进,直到再次覆盖住所有知识点。记录每次t-s的最小值,不断更新即可。
此算法的时间复杂度为O(NlogN)。
代码备注:
先用set,O(n)统计出知识点的个数,再使用map存储各个知识点出现的次数。
i,j为尺子的头尾
mi为当前区间包含的知识点个数
m为需要学习的知识点个数
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
int a[1000010];
int min(int a,int b){
if (a<b) return a; else return b;
}
int main()
{
int n,m;
scanf("%d",&n);
set<int>s;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s.insert(a[i]);
}
/*当j<n并且知识点还没有覆盖完全即(mi<m)时,
我们需要一直增加j,并且中间记录各知识点出现的次数。
执行完此次循环之后,我们一定会得到一个答案(不一定最优),所
以可以更新答案。*/
m=s.size();//知识点个数
map<int,int>mp;
int i=1,j=1,mi=0,ans=n;
while (1){
while (j<=n&&mi<m){
if (mp[a[j]]==0) mi++;
mp[a[j]]++;
j++;
}
if (mi<m) break;
/*若直至j出界,知识点个数还是不够,说明结果统计完毕,此处为循环出口*/
ans=min(ans,j-i);
mp[a[i]]--;
if (mp[a[i]]==0) mi--;
i++;
/*增加i,答案继续更新,若--mp[a[i]]==0了,
就需要再增加j以保证所有知识点的全覆盖。*/
}
printf("%d\n",ans);
}
声明:此文章为半原创,是在读完大佬的文章,理解领悟之后写的。
原文https://blog.csdn.net/zwj1452267376/article/details/50505890