尺取法入门题集:常用来解决区间有一定规律的题目。
- 最小的区间满足和≥s
- 最小的区间包含所有的种类
- 多少区间的第k大的数≥m
- 多少素数序列满足和等于n
- 多少连续的数的平方和等于n
POJ3061
题意:最短的子串满足和≥s
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int const inf = 0x3f3f3f3f; //定义无穷大
int a[N],n,s;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int st = 1,sum = 0,ans = inf;
for(int i=1;i<=n;i++){
sum += a[i];
while(sum >= s){
ans = min(ans,i - st + 1);
sum -= a[st++];
}
}
if(ans == inf) ans = 0;
printf("%d\n",ans);
}
return 0;
}
HDU5806
题意:求有多少区间的第k大的数≥m。
题解:
- 如果某个序列含有k个数大于等于m,那么可以保证加上后面的数,还是满足第k大的数≥m。
- 采用尺取法,从头到尾扫描,每找到一段区间[l,r],满足有k个数大于等于m,那么区间[l,r...n]都是满足条件的,有n - r + 1种。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 2e5 + 10;
int n,m,k,a[N];
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int cnt = 0,st = 1;
long long ans = 0; //注意开long long
for(int i=1;i<=n;i++){
if(a[i] >= m) cnt++;
while(cnt == k){
ans += (n - i + 1);
if(a[st++] >= m) cnt--;
}
}
printf("%lld\n",ans);
}
return 0;
}
CF701C
题意:最短的子串包含字符串所有种类的字母。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int const inf = 0x3f3f3f3f;
int n,num[250];
char s[N];
int main(){
scanf("%d",&n);
scanf(" %s",s);
int k = 0;
for(int i=0;i<n;i++) //k表示字母总数
if(++num[s[i]] == 1) k++;
memset(num,0,sizeof(num));
int cnt = 0,st = 0;
int ans = inf;
for(int i=0;i<n;i++){
if(++num[s[i]] == 1) cnt++;
while(cnt == k && st <= i){
ans = min(ans,i - st + 1);
if(--num[s[st]] == 0) cnt--;
st++;
}
}
printf("%d\n",ans);
return 0;
}
POJ2739
题意:多少素数序列满足和等于n
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int const N = 10000;
int vis[N+10],prime[N+10],len;
void prime_table(){
for(int i=2;i<N;i++){
if(!vis[i]) prime[++len] = i;
for(int j=1;j<=len&&prime[j]*i<N;j++){
vis[prime[j]*i] = true;
if(i % prime[j] == 0) break;
}
}
}
int main(){
prime_table();
int n;
while(~scanf("%d",&n) && n){ //n可以分解成多少个素数的和
int ans = 0,sum = 0,st = 1;
for(int i=1;i<=len;i++){
sum += prime[i];
while(sum >= n){
if(sum == n) ans++;
sum -= prime[st++];
}
}
printf("%d\n",ans);
}
return 0;
}
POJ2100
题解:多少连续的数的平方和等于n
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
int const N = 100000 + 10;
ll n;
pii ans[N];
int main(){
scanf("%lld",&n);
ll sum = 0;
int st = 1;
int cnt = 0;
for(int i=1;i<=1e7;i++){
sum += 1ll*i * i;
if(i * i > n) break;
while(sum >= n){
if(sum == n) ans[++cnt] = pii(st,i);
sum -= 1ll*st * st;
st++;
}
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d",ans[i].second - ans[i].first + 1);
for(int j=ans[i].first;j<=ans[i].second;j++)
printf(" %d",j);
printf("\n");
}
return 0;
}