SubsequencePOJ3061
题意:
求出总和不小于S的连续子序列的长度的最小值。
思路:
二分查找的话,前缀和是满足单调性的,计算从每一个数开始总和刚好大于s的长度。
具体实现就是:二分搜索s[i]+s是否存在于前缀和数组中,就是查找以i+1开头的总和刚好大于s的最短长度。
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; const int maxn=1e5+5; int sum[maxn]; int main() { int t; cin>>t; while(t--) { int n,S; cin>>n>>S; for(int i=0;i<n;i++) { int x; cin>>x; sum[i+1]=sum[i]+x; } if(sum[n]<S) { puts("0"); continue; } int res=n; for(int s=0;sum[s]+S<=sum[n];s++) { int t=lower_bound(sum+s,sum+n,sum[s]+S)-sum; res=min(res,t-s); } cout<<res<<endl; } return 0; }
尺取法:
Jessica's Reading ProblemPOJ3320
题意:看最小的页数,获得整本书的所有知识点。求这个页数是多少。(页数必须连续)
思路:所有知识点都被覆盖==每个知识点出现的次数都不小于1
可以利用二叉树等数据结构来存储[s,t]区间上每个知识点的出现次数。
从区间的最开头把s去除之后,s上书写的知识点的出现次数就要减一,如果此时这个知识点的出现次数为0,在同一个知识点再次出现前,不停将区间末尾t向后推进。
时间:O(PlogP)
①用利用set集合里面元素的唯一性,将a[i]的所有元素放到里面去,这样就知道了一共有几个知识点。
②尺取法的原理是从起始left到末端right中寻找答案,然后利用map的映射,获得每个知识点所出现的数目即可。
③退出条件是sun < 知识点数目。
#include #include #include #include #include using namespace std; int p; int a[1000000 + 10]; void solve(){ set ind; for (int i = 0; i < p; i++){ ind.insert(a[i]); } int left = 0, right = 0, sum = 0; map ans; int res = p;//假设全部都看一遍 while (true){ while (right < p && sum < ind.size()){ //printf("ans[] = %d\n", ans[a[right]]); if (ans[a[right++]]++ == 0){ sum++; } } //printf("sum = %d\n", sum); if (sum < ind.size()) break; res = min(res, right - left); if (--ans[a[left++]] == 0){//注意,这里不论if条件是否成立,--ans[a[left++]]的加减都会执行 sum--; } } printf("%d\n", res); } int main(){ while(scanf("%d", &p) != EOF){ memset(a, 0, sizeof(a)); for (int i = 0; i < p; i++){ scanf("%d", a + i); } solve(); } return 0; }