最近做了一些关于尺取法的题,大概明白了一些套路。
首先固定左端点,不断地去取右端点,直到区间尽头或者不满足条件,然后判断是否满足条件,如果不满足条件一般情况下是到了尽头,然后去最值,然后删去一个左端点再在新的一个左端点上不断地去取右端点,这样整个算法的复杂度为O(n)
POJ 3061
尺取法版本:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
// #define test TEST
using namespace std;
typedef long long ll;
const int maxn=100005;
int a[maxn],sum[maxn],n,s;
void solve(){
int pa=1,pb=1,temp=0,ans=0x3f3f3f3f;
while(true){
while(pb<=n&&temp<s){
temp+=a[pb++];
}
if(temp<s) break;
ans=min(ans,pb-pa);
temp-=a[pa++];
}
if(ans==0x3f3f3f3f) printf("0\n");
else printf("%d\n",ans);
}
int main(int argc, char const *argv[])
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&s);
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
solve();
}
return 0;
}
另一个版本:二分法+尺取
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
// #define test TEST
using namespace std;
typedef long long ll;
const int maxn=100005;
int a[maxn],sum[maxn],n,s;
void solve(){
if(sum[n]<s){
printf("0\n");
return ;
}
int ans=0x3f3f3f3f;
for(int i=1;sum[i]+s<=sum[n];i++){
int t=lower_bound(sum+i,sum+n+1,sum[i]+s)-sum;
ans=min(ans,t-i);
}
printf("%d\n",ans);
}
int main(int argc, char const *argv[])
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&s);
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
solve();
}
return 0;
}
另一道题,POJ 3320 需要map去做离散化
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
using namespace std;
const int maxn=1000005;
int num[maxn];
int main(int argc, char const *argv[])
{
ios::sync_with_stdio(false);
int n;
while(~scanf("%d",&n)){
map<int,int>s;
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
s[num[i]]++;
}
int ans=0x3f3f3f3f;
int len=s.size();
int l=1,r=1,temp=0;
map<int,int>vis;
while(true){
while(r<=n&&temp<len){
if(vis.find(num[r])==vis.end()||!vis[num[r]]){
temp++;
}
vis[num[r++]]++;
}
if(temp<len) break;
ans=min(ans,r-l);
if(--vis[num[l]]==0){
temp--;
}
l++;
}
cout<<ans<<endl;
}
return 0;
}