题目链接:http://poj.org/problem?id=3320
题目大意:一本书上有n个知识点,这本书一共有p页,每页一个知识点(每个知识点有一个唯一的整数编号),全书中同一知识点可能会被多次提到,现在希望通过阅读其中连续的一些页码把所有的知识点都覆盖到。给出每页写到的知识点,求出最少需要阅读的页数。
分析:我们假设从某一页(第s页)开始阅读,为了覆盖所有的知识点需要阅读到第t页。这样的话可以知道如果从第s+1页开始阅读的话,那么必须阅读到t'>=t页为止。我们用尺取法,在某个区间[s,t]已经覆盖了所有知识点的情况下,来求下一个区间[s+1,t']。我们有“所有的知识点都被覆盖<=>每个知识点出现的次数都不小于1”这一等价关系,可以由二叉树等数据结构来存储[s,t]区间上每个知识点出现的次数,这一把最开头的第s页去掉后,同一个知识点再次出现前,不停的将区间向后推进即可。每次在区间末尾追加第t'页将第t'页上的知识点出现的次数加1,这一就完成了下一个区间上各个知识点出现次数的更新。通过重复这一操作可以在O(PlogP)的复杂度内求出最小区间。
实现代码如下:
#include <cstdio>
#include <set>
#include <map>
using namespace std;
const int M=1000005;
int b[M],p;
int min(int x,int y)
{
return x<y?x:y;
}
void solve()
{
set <int> all;
for(int i=0;i<p;i++)
all.insert(b[i]);
int np=all.size();
//尺取法
int ans=p;
int s=0,t=0,num=0;
map <int,int> count; //知识点->出现次数的映射
while(true)
{
while(t<p&&num<np)
if(count[ b[t++] ]++==0) num++;
if(num<np) break;
ans=min(ans,t-s);
if(--count[ b[s++] ]==0) num--;
}
printf("%d\n",ans);
}
int main()
{
while(scanf("%d",&p)!=-1)
{
for(int i=0;i<p;i++)
scanf("%d",&b[i]);
solve();
}
return 0;
}