尺取法
一种高效的枚举区间的方法,比直接暴力效率高了很多。
复杂度:O(n)
证明:
设区间的左端点为l,右端点为r
最坏的情况就是l遍历了1-n,r也遍历了1-n
总的复杂度为2*n(原因在于尺取法内部l与r的循环不是嵌套的,虽然表面上是嵌套)
解决问题类型:
1.满足一定条件的区间个数问题
2.满足一定条件的区间最优解问题
核心思想:
1.初始左右端点
2.不断右移右端点,知道满足条件
3.如果第二步无法满足条件,终止操作,否则更新结果
4.右移左端点,返回第二步
/***********************************************************************************************************************************************/
poj3061
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,s;
scanf("%d%d",&n,&s);
int a[100010];
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int sum=0;
int len=INF;
int l=1,r=1;
while(1)
{
while(r<=n&&sum<s)
sum+=a[r++];
if(sum<s)
break;
len=min(len,r-l);
sum-=a[l++];
}
if(len==INF)
printf("0\n");
else
printf("%d\n",len);
}
return 0;
}
poj3320
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define INF 0x3f3f3f
int main()
{
set<int> q;
map<int,int> p;
int pp;
scanf("%d",&pp);
int a[1000010];
for(int i=1;i<=pp;i++)
{
scanf("%d",&a[i]);
q.insert(a[i]);
}
int num=q.size();
int l=1,r=1;
int sum=0;
int len=INF;
while(1)
{
while(r<=pp&&sum<num)
{
if(p[a[r++]]++==0)
sum++;
}
if(sum<num)
break;
len=min(len,r-l);
if(--p[a[l++]]==0) sum--;
}
printf("%d\n",len);
return 0;
}