尺取:顾名思义,就是拿一把尺子量取一段距离,然后就是不断的推进左右端点,进而将复杂度控制在O(n)范围内
下面我们可以做几道例题:
Subsequence
题意:就是找到长度最小的连续序列,加起来大于等于s。
这就是用到了尺取(很简单),下面是AC代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
#define int long long
int a[100010];
signed main()
{
int t;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--)
{
int n,s;
cin >> n >>s;
for (int i = 1; i <= n; i++) cin>>a[i];
int cnt=0,sum=0,pos=1;
int ans=1e18;
for(int i=1;i<=n;i++)
{
while(sum<s&&cnt<n)
{
sum+=a[++cnt];
}
while(sum-a[cnt]>=s&&cnt>i)
{
sum-=a[cnt];
cnt--;
}
if(sum>=s)
{
ans=min(ans,cnt-i+1);
}
sum-=a[i];
}
if(ans<1e18) cout<<ans<<endl;
else cout<<0<<endl;
}
return 0;
}
题意:找到最小的区间能够包含数列中出现的所有的数
题解:也是很简单,就是注意一下本题的数据范围是爆int的
下面是AC代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <set>
#include<map>
using namespace std;
#define int long long
int a[1000100];
map<int,int> num;
signed main()
{
int t;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
set<int> se;
int n, s;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i],se.insert(a[i]);
int len=se.size();
int cnt=0,pos=0,ans=n;
for (int i = 1; i <= n; i++)
{
while(pos<n&&cnt<len)
{
if(num[a[++pos]]==0)
{
num[a[pos]]++;
cnt++;
}
else num[a[pos]]++;
}
while(cnt==len&&pos>i)
{
if(num[a[pos]]>1)
{
num[a[pos]]--;
pos--;
}
else break;
}
if(cnt==len) ans = min(ans, pos - i + 1);
num[a[i]]--;
if(num[a[i]]==0) cnt--;
}
cout << ans << endl;
return 0;
}
例题3:
Bound Found
这个题就是找到区间和与给定的数最接近的区间,但是改了很长时间也没有改对,网上的代码也是错的,但是思想是正确的。
就是这个题因为是让求绝对值,所以我们可以用前缀和的思想,因为两个前缀和之差就是一段区间内的和,我们将其从小到达排序,这样就能保证他的单调性。单调性是前缀和排序后最优的性质,所以我们这样就可以运用一般的尺取方法从前往后维护>x的第一个值,和<x的最后一个值,或者是=x那个值。下面是半成品代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define int long long
const int N=100010;
struct node
{
int v;
int p;
}a[N];
bool cmp(const node &w1,const node &w2)
{
return w1.v<w2.v;
}
signed main()
{
int n,m;
while(cin>>n>>m,n+m)
{
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
a[i].p=i;
a[i].v=a[i-1].v+x;
}
sort(a+1,a+1+n,cmp);
while(m--)
{
int x;
cin>>x;
int pos=1;
int ansl,ansr,len=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
pos=max(pos,i);
while(a[pos].v-a[i-1].v<x&&pos<n)//维护到第一个大于他的值或等于的值
{
if(abs(len-x)>abs(a[pos].v-a[i-1].v-x))
{
len=abs(a[pos].v-a[i-1].v);
ansl=a[i-1].p+1,ansr=a[pos].p;
}
pos++;
}
if(a[pos].v-a[i-1].v==x)
{
len=x;
ansl=a[pos].p,ansr=a[i-1].p+1;
//break;
}
if(a[pos].v-a[i-1].v>x)
{
if(abs(len-x)>abs(a[pos].v-a[i-1].v-x))
{
len=a[pos].v-a[i-1].v;
ansl=a[i-1].p+1,ansr=a[pos].p;
}
pos--;
}
}
if(ansl>ansr) swap(ansl,ansr);
cout<<len<<" "<<ansl<<" "<<ansr<<endl;
}
}
return 0;
}
例题4:Graveyard Design
题意:就是求某个数是多少个连续的数的平方和,并将所有的情况求出来。一般尺取,注意下标即可,下面是AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
const int N=1e7+10;
vector<pair<int,int> > vec;
signed main()
{
int x;
cin>>x;
int pos=1;
int sum=0;
int up=sqrt(x);
for(int i=1;i<=up;i++)
{
while(sum+pos*pos<x&&pos<up)
{
sum+=pos*pos;
pos++;
}
if(sum+pos*pos==x)
{
pair<int,int> pii;
pii.first=i;
pii.second=pos;
vec.push_back(pii);
}
sum-=i*i;
}
cout<<vec.size()<<endl;
for(int i=0;i<vec.size();i++)
{
int x=vec[i].first;
int y=vec[i].second;
cout<<y-x+1;
for(int j=x;j<=y;j++)
{
cout<<" "<<j;
}
cout<<endl;
}
return 0;
}
例题5:Sum of Consecutive Prime Numbers
题意:就是找出一个数字可以用x个连续数表达的形式的个数。
题解:先把所有的质数筛出来,然后就是按照普通的来就行了。
下面是AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10;
bool st[N];
int prime[N],cnt;
void is_prime(int n)
{
int i,j;
for(i=2;i<=n;i++)
{
if(!st[i]) prime[++cnt]=i;
for(j=1;j<=cnt&&prime[j]<=n/i;j++)
{
st[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
int main()
{
int n;
is_prime(10010);
while(cin>>n,n)
{
int pos=1;
int ans=0;
int sum=0;
for(int i=1;i<=cnt;i++)
{
while(sum+prime[pos]<n&&pos<cnt)
{
sum+=prime[pos];
pos++;
}
if(sum+prime[pos]==n)
{
ans++;
}
sum-=prime[i];
}
cout<<ans<<endl;
}
}
例题6:戳我
例题6的题目和题解都在上面了,是一道尺取+二分的题目