POJ3320 Jessica's Reading Problem 尺取法

题目链接: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;
}


你可能感兴趣的:(POJ3320 Jessica's Reading Problem 尺取法)