尺取法,顾名思义和尺子一样,一段一段的量过去。和遍历有点类似,但是却时间快于遍历。
看到别人的博客,说尺取法和蚯蚓蠕动类似,先从起点开始,然后直到头到最大范围(即身子拉长到极限),然后在收缩尾巴,再动头部,向前伸展。
类似的把蚯蚓的尾巴看做数组的起点,把头部看做遍历的进程。
下面介绍一题,尺取法的入门题。 poj 3061
Description
Input
Output
Sample Input
2 10 15 5 1 3 5 10 7 4 9 2 8 5 11 1 2 3 4 5
Sample Output
2 3
ac代码: // 另外补一句,遇见这个容易超时的,尽量不要用cin cout 改为scanf 和printf 。
#include<iostream>
#include<cstring>
#define min(a,b) a>b?b:a
using namespace std;
int a[100010];
int main()
{
int t,n,m,i,j,sum;
cin>>t;
while (t--)
{
sum=0;
cin>>n>>m;
for (i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
}
if (sum<m)
{
cout<<0<<endl;
continue;
}
sum=0;
int l=0,r=0,res=n+1;
while (r<n)
{
while (sum<m&&l<n)
sum+=a[l++];
if (sum<m)
break;
res=min(res,l-r);
sum-=a[r++];
}
cout<<res<<endl;
}
return 0;
}
链接:https://www.nowcoder.com/acm/contest/84/F
来源:牛客网
题目描述
输入描述:
第一行两个整数n,d(1 <= n <= 100,000,1 <= d <= 1000,000,000); 第二行n个整数满足abs(ai) <= 1,000,000,000。数据保证a单调递增。
输出描述:
输出一个整数表示满足条件的选法。
链接:https://www.nowcoder.com/acm/contest/84/F
来源:牛客网
输入
4 3 1 2 3 4
输出
4
输入
4 2 -3 -2 -1 0
输出
2
输入
5 19 1 10 20 30 50
输出
1
解法一 ac:
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll a[100010];
int main()
{
int n;
ll d,i,res=0,temp,l,r;
scanf ("%d%lld",&n,&d);
for (i=1;i<=n;i++)
scanf ("%lld",&a[i]);
for (i=1;i<=n;i++) // 尺取法 (思路简单,但是我写的比较复杂)
{
l=i;
if (l>=n-1)
break;
if (l==1)
r=3;
if (r<=n&&a[r]-a[l]<=d)
{
while (a[r]-a[l]<=d&&r<=n)
r++;
r--;
res=res+(r-l)*(r-l-1)/2;
}
else if (l<n-1&&a[l+2]-a[l]>d)
continue;
else if (a[r]-a[l]>d)
{
while (a[r]-a[l]>d)
r--;
}
}
cout<<res<<endl;
return 0;
}
解法二 ac:
#include<iostream>
#include<cstdio>
#define ll long long
ll a[100010];
int main()
{
int i,n;
ll d,res,mid,temp;
while (scanf ("%d%lld",&n,&d)!=EOF)
{
res=temp=0;
for (i=1;i<=n;i++)
scanf ("%lld",&a[i]);
for (i=1;i<=n;i++) // 二分
{
ll l=i,r=n;temp=0;
while (l<=r)
{
mid =(l+r)>>1;
if (a[mid]-a[i]<=d)
{
temp=mid;
l=mid+1;
}
else
r=mid-1;
}
if (temp!=0&&temp>(i+1))
res=res+(temp-i-1)*(temp-i)/2;
}
printf ("%lld\n",res);
}
return 0;
}