poj3061 Subsequence 复习二分搜索+前缀和最小→尺取法

二分搜索:

前些天,把二分搜索某些细节想复杂了。

①其实二分只要固定个人写法就没问题(整数题目一般取左端点left,与题目取最大值还是最小值无关)

②判断条件的大于小于等于号也没有那么复杂,特判一下等号和题目让你最大化还是最小化而已。

③难在判断条件函数绕了个弯,要自己建立另一个变量,使这个变量和二分搜索的量形成单调关系


前缀和:sum数组,从1开始

最小值:res,min(res,xxx)


区间问题很容易先想到暴力,但暴力要i,j两个for,复杂度O(n^2),所以考虑优化,由于二分可以实现固定一个端点,移动另一个端点找。(二分只能改变一个变量)

以下是二分搜索代码,复杂度O(nlogn),141ms,主要是不是最优:


#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
int a[1000004];
int sum[1000005];
int n,s;


int main()
{
int T;
cin>>T;
while(T--)
{
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));

scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];//前缀和 
}
if(sum[n]<s)
cout<<"0"<<endl;
else
{
int res=1000000000;//取最小值套路
int l;
int r;
int mid;

int i;
for( i=1;sum[n]-sum[i]>=s;i++)//结束条件很关键,如果取不到和S,就不要往后面找了 
{
l=i;
r=n;
while(l<=r)
{
mid=(l+r)/2;
if(sum[mid]-sum[i]<s)
l=mid+1;
else    //就算等于,我还想找更短的 
r=mid-1;
}
//cout<<l-i<<endl;
res=min(res,l-i);
}
cout<<res<<endl;

}


}


接下来是尺取法,O(n)复杂度,79ms!可以实现两个端点的移动,其实仔细想想,尺取法和二分搜索思路很像!

以本题找最小值为例:

               满足条件                            不满足条件

二分       缩小范围,r指针向左          扩大范围,l指针向右

尺取法    缩小范围,左指针右移        扩大范围,右指针右移

找最大值反之。


注意:

①区间求和和端点移动先后次序 !!!a[s++]和a[++s],以后莫队也会用到。

②关于区间问题,有些要注意临界端点的问题。(以后有再提)


代码如下:


#include<iostream>
#include<stdio.h>
using namespace std;
int a[10000005];
int n,s;


int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%d%d",&n,&s);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);

}

int l,r;
l=r=0;
int sum=0;
int res=1000000000;
for(;;)
{
while(sum<s&&r<n)  //右端点左移 
{
sum+=a[r];
r++;          //区间移动和端点移动先后次序 !!!
}
if(sum<s)break;

res=min(res,r-l);
   
sum-=a[l];         //左端点右移,不用再写个while(sum>=s),多此一举 
l++;
}
if(res>n)
cout<<"0"<<endl;
else
cout<<res<<endl;


}


}













猜你喜欢

转载自blog.csdn.net/zjyang12345/article/details/80328746