题目链接:
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; }
Subsequence
题意:给定一个序列,求出总和不小于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; }