题目链接:
http://poj.org/problem?id=3320
题意:
一本书有p页,每一页都有一个知识点ai,存在不同两页上的知识点相同的情况,
求最少读连续的多少页书,能把所有的知识点全部覆盖到。
尺取法:
反复的推进区间的开头和结尾,来求满足条件的最小区间的方法被称为尺取法
尺取法的复杂度为O(n).
初始化,start ,end ,sum =0;
然后推进end 直到恰好满足条件退出,如果区间已经到了结尾,仍不满足,则退出
然后更新答案 ans = min(ans,end-start);
然后start推进一位,要处理这时对sum的影响。
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
using namespace std;
const int maxn = 1e6+10;
int a[maxn];
int main()
{
int n;
while(~scanf("%d",&n)){
map<int ,int >mp;
int num = 0;
for(int i=0;i<n;i++){
scanf("%d",a+i);
if(!mp[a[i]]) num++;
mp[a[i]]++;
}
int st=0,t=0,sum=0,ans = n;
mp.clear();
while(1){
while(t<n&&sum<num){
if(!mp[a[t++]]++)
sum++;
}
if(sum<num) break;
ans = min(ans,t-st);
if(--mp[a[st++]]==0)
sum--;
}
printf("%d\n",ans);
}
return 0;
}
类似的题目还有POJ 3061
题意:给定一个序列,求出总和不小于S的连续子序列的长度的最小值
这题有两种方法。
方法一:
预处理出,sum[i]=a[0]+....+a[i-1];
然后在区间[i,n]二分查找大于等于sum[i]+S的最小值的下标,
ans=min(ans,pos-i);
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
int a[maxn],sum[maxn];
int main()
{
int t,s,n;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&s);
sum[0]=0;
for(int i=0;i<n;i++){
scanf("%d",a+i);
sum[i+1]=sum[i]+a[i];
}
if(sum[n]<s){
printf("0\n");
continue;
}
int ans = n;
for(int i=0;sum[i]+s<=sum[n];i++){
int pos=lower_bound(sum+i,sum+n,sum[i]+s)-sum;
ans = min(ans,pos-i);
}
printf("%d\n",ans);
}
return 0;
}
方法二,
尺取法
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1e5+10;
int a[maxn];
int main()
{
int t,n,s;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&s);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int st=0,t=0,sum=0,ans= n+1;
while(1){
while(t<n&&sum<s)
sum+=a[t++];
if(sum<s) break;
ans=min(ans,t-st);
sum-=a[st++];
}
if(ans>n){
printf("0\n");
continue;
}
else
printf("%d\n",ans);
}
return 0;
}