【C++心路历程26】考试复习【尺取法】poj3320

【问题描述】

  为准备考试,Jessica开始阅读一本很厚的课本。要想通过考试,必须把课本中所有知识点都掌握。这本书总共有P页,第i页恰好有一个知识点a[i](每个知识点都有一个整数编号)。全书中同一个知识点可能会被多次提到,所以她希望通过阅读其中连续的一些页把所有的知识点都覆盖到。给定每页写到的知识点,请求出要阅读的最少页数。

【输入格式】

  第一行一个整数P,表示书本的总页数。接下来的一行,包含P个整数,第i整数表示第i页包含的知识点的编号。

【输出格式】

  一个整数,表示需要阅读的最少连续页数。

【输入样例】

5
1 8 8 8 1

【输出样例】

2

【数据范围】

1<=P<=10^6 , 1<=a[i]<=10^9

【分析】
本题要求的是一个最长的子序列,能包含原序列所有的公共元素。
暴力算法:枚举子序列左端点和右端点,再检查。时间复杂度O(P4)(指数为什么打不出来、、、)
跟以前一些题类似的是滑动窗口,但这个题要维护的窗口不是最大值或最小值,而是维护窗口内的最短连续子序列长度,且要包含尽量多的不同元素。可以想到用尺取法来做。
思路:
int L=1,ans=inf,cnt2=0;
cnt表示给出序列内不同元素个数。
cnt2表示当前窗口内不同元素个数。
枚举右端点R,用map来存储每一个元素的出现次数,如果出现cnt2==cnt,与ans相比较。每当右端点扩展1,判断左端点是否重复(左端点位置的移动一定是在左端点元素被重复(出现第2次)后才可能的
这里附一个hash表的程序,其思路与使用map是大致的。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<set>
#include<map>
using namespace std;
const int maxn=1000005;
const int inf=10000005;
int n,cnt=2;
int a[maxn],vis[maxn],b[maxn];
int main()
{
// freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    **//离散化**
    memcpy(b,a,sizeof(b));
    sort(b+1,b+n+1);
    for(int i=2;i<=n;i++)
    {
        if(b[i]!=b[i-1])    b[cnt++]=b[i];
    }
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+cnt,a[i])-b;
    memset(vis,0,sizeof(vis));

    int L=1,ans=inf,cnt2=0; 
    for(int R=1;R<=n;R++)
    {
        vis[a[R]]++;
        if(vis[a[L]]==1)
        {
            if(vis[a[R]]==1)    cnt2++;//新的元素
            if(cnt2==cnt-1)//判断 //cnt要多一个
            ans=min(ans,R-L+1);
        }

        else
        {
            while(vis[a[L]]>1)  //删除左端重复元素
            {
                vis[a[L]]--;
                L++;
            }
            if(cnt2==cnt-1)//判断
            ans=min(ans,R-L+1);
        }   

    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(C语言,poj)