POJ 3320 · Jessica's Reading Problem【尺取法】

【题意】

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

【提炼】

计算一段连续的包含所有知识点的子序列的最小长度。(知识点至少出现一次)

【分析】

连续求最小长度,除了dp,首先想到尺取法。

将原问题:求覆盖到所有知识点区间[s,t]的长度的最小值,

根据尺取法的特点,可以将问题转换为:

已知[s,t],求下一个区间覆盖到所有知识点的区间[s+1,t],这时,必须满足tt

<==>1

由以上等价关系,将问题转换为:
从区间的最开始把s取出之后,直到某个知识点出现次数变为0,然后不停将区间末尾t向后推进,重新计算知识点出现的次数以及记录满足条件的最小长度。最终,当t到达数据尾部,算法执行结束。

暴力需要O(n2)

用map存储[s,t]区间上的每个知识点的出现次数,问题又转化为:
从区间的最开始把s取出之后,将该知识点出现次数减1,若该知识点出现次数为0,在同一知识点再次出现前,不停地将区间末尾t向后移动。每次在区间末尾追加页数t时将t所在的知识点的出现次数加1,这样就完成了下一个区间上各知识点的出现次数的更新。最后,遍历一遍全部区间求出最小区间需要O(nlogn)

【时间复杂度】

由于使用了二叉树的存储结构来保存每个知识点的出现次数,时间复杂度为

O(nlogn)

【代码】

/*
    coder:  Tangent Chang
    date:   2017/5/9
    A day is a miniature of eternity. by Emerson
*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <set>
#include <map>

using namespace std;

const int maxn = 1000005;

int d[maxn];
set<int> ST;
map<int , int> MP;

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &d[i]);
        ST.insert(d[i]);
    }
    int tmp = n;
    n = ST.size();
    int s = 0, t = 0, num = 0, res = tmp + 1;
    while (1) {
        while (t < tmp && num < n) {
            if (MP[d[t++]]++ == 0) {
                num++;
            }
        }
        if (num < n) break;
        res = res < t - s ? res : t - s;
        if (--MP[d[s++]] == 0) {
            num--;
        }
    }
    printf("%d\n", res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值