poj3320(尺取法+离散化)

Jessica's Reading Problem

题意:

  一本书有若干个知识点,不同的数字代表不同的知识点,要求找出一个连续子区间包含所有的知识点,求这个子区间的最小长度是多少?

分析:

  首先想到的是枚举每个点作为左端点,利用尺取法找到其右端点。考虑用vis数组标记已经学习过的知识点,然后我们在向右搜索时就能知道什么知识点我们还没学习过。用pos数组标记 i 知识点我们是在哪一点学习的,这样当我们左端点右移时,就知道之前的左端点对应的知识点是否唯一。因为对于每个知识点,我们不关心它的值是多少,只需要考虑两个知识点是否为相同的知识点,所以我们就可以把所有数据离散化。

代码:

#include <map>
#include <math.h>
#include <string>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;
#define ll long long
#define ull unsigned long long
#define cls(x) memset(x,0,sizeof(x))
#define clslow(x) memset(x,-1,sizeof(x))

const int maxn=1e6+100;

int n;

bool vis[maxn];
int a[maxn],b[maxn],pos[maxn];

int main()
{
//    freopen("in.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        cls(vis);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }

        sort(b+1,b+n+1);
        //一共有多少知识点
        int sz=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(b+1,b+n+1,a[i])-b-1;
        }

        int s=1,e=1;
        int cnt=0,ans=n+1;
        while(true)
        {
            //向右移动找到右端点
            while(e<=n&&cnt<sz){
                pos[a[e]]=e;
                if(!vis[a[e]]){
                    cnt++;
                    vis[a[e]]=true;
                }
                e++;
            }
            if(cnt<sz)  break;
            ans=min(ans,e-s);
            //如果左端点对应的知识点唯一
            if(pos[a[s]]==s){
                cnt--;
                vis[a[s]]=false;
            }
            s++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/shutdown113/p/9385076.html
今日推荐