POJ 3320 Jessica's Reading Problem ( 尺取法 )

题意 : 给你一本书、整本书有若干个不同的知识点,书的每一页都包含了一个知识点 ( 包含的知识点可能与其他页重复 ),问你如果取连续的几页用来读完书中包含的所有知识点,那么这连续的几页最少是多少?( 书的页数 ≤ 1e6 )

分析 : 最直接的想法就是先 O(n) 地枚举页首,然后计算贡献,在过程中维护最小值即可。但是这样做最差的情况就可能达到 O(n^2) 因此需要优化。那现在来考虑一下如果当前枚举到 st 且符合条件的页尾为 en ,那么其贡献就是 en - st + 1,接下来枚举 st+1 的时候会发现其符合条件的页尾 (令为 en') 必定不小于 en,即 en' ≥ en。道理应该不难理解,因为如果当前从 st 到 st+1 则自然少了 st 这一页包含的知识点,而 st 这一页作为页首符合条件的页尾都要达到 en ,那么少了一个知识点的 st+1 作为页首作为符合条件的相应页尾必然不小于 en ,因此此题使用尺取法可行!具体实现就看代码吧!

#include<stdio.h>
#include<map>
#include<set>
using namespace std;
const int maxn = 1e6 + 10;
int Page[maxn], num[maxn];
set<int> Have;
map<int, int> mp;
int tot;
int main(void)
{
    int n;
    while(~scanf("%d", &n)){

        mp.clear();
        tot = 1;
        for(int i=1; i<=n; i++){
            num[i] = 0;
            scanf("%d", &Page[i]);
            if(!mp.count(Page[i]))
                mp[Page[i]] = tot++;
        }

        int L, R, ans;
        L = R = 1;
        ans = n;

        Have.clear();
        Have.insert(mp[Page[L]]);
        num[mp[Page[L]]]++;
        while(L <= R && R <= n){
            if((int)Have.size() == tot-1){
                ans = min(ans, R-L+1);
                if(--num[mp[Page[L]]] == 0)
                    Have.erase(mp[Page[L]]);
                L++;
            }else{
                R++;
                Have.insert(mp[Page[R]]);
                num[mp[Page[R]]]++;
            }
        }
        printf("%d
", ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/qwertiLH/p/8037143.html