[题目链接] : https://vjudge.net/contest/279505#overview
1.尺取
尺取法 :又叫做双指针,适合用来求一段连续的子区间
可以在O(n)的时间得出类似的求一段连续子区间的解
接下来让我们来看看模板题
A题:在一段n个正整数的序列中,找个一段连续大于等于m的子序列
题解:直接套模板
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100005];
int main()
{
int T;
cin>>T;
while(T--)
{
int n,s;
cin>>n>>s;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
int i=0,j=0,ans=100005,sum=0;
while(1)
{
while(sum<s&&j<n)
{
sum+=a[j++];
}
if(sum<s)
break;
ans=min(ans,j-i);
sum-=a[i++];
}
if(ans<100005)
{
cout<<ans<<endl;
}
else cout<<"0"<<endl;
}
return 0;
}
B题:求1-n中哪一段子区间的平方和等于S
题解 :此题相比较上一题需要注意对每一种可能进行储存,可以开一个结构体数组进行储存
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
struct node
{
int x,y;
}t[1000005];
int main()
{
ll n;
while(scanf("%lld",&n)!=EOF)
{
ll i=1,j=1,sum=0,ans=0;
while(i*i<=n)
{
while(sum<n&&j*j<=n)
{
sum+=j*j;
j++;
}
if(sum==n)
{
ans++;
t[ans].x=i;
t[ans].y=j;
}
sum-=i*i;
i++;
}
cout<<ans<<endl;
for(int i=1;i<=ans;i++)
{
cout<<t[i].y-t[i].x;
for(int j=t[i].x;j<t[i].y;j++)
{
cout<<" "<<j;
}
cout<<endl;
}
}
return 0;
}
2. 二分
二分的复杂度可以将复杂度降为log2 n
需要注意的是应用二分需要是一段具有单调性的区间,每次取l,r中点与所找的元素比较,若过大,则右边界变为中点,反之则左边界变为中点,直至找到答案
二分的难点主要在于如何设置判断条件
看题
C题意:给出n个位置,选出其中m个位置,使得m个位置之间的最短距离最大。
题解:先对位置进行排序,使左边界为0,右边界为大于距离最大的可能。然后不断二分直到符合条件
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100005];
int n,c;
bool check(int mid)
{
int temp=a[1],sum=1;
for(int i=2;i<=n;i++)
{
if(a[i]-temp>=mid)
{
temp=a[i];
sum++;
}
}
if(sum>=c)return 1;
else return 0;
}
int main()
{
cin>>n>>c;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+1+n);
ll l=1,r=a[n]-a[1];
while(l<=r)
{
ll mid=(l+r)/2;
if(check(mid))
{
l=mid+1;
}
else r=mid-1;
}
cout<<l-1<<endl;
}
D题:一根棍子横立与两面墙之间,当棍子受热膨胀后会弯成弓形
(可以看做圆弧的一部分),求弯曲的棍子与原棍子的中点的距离。
这道题考察二分与计算几何,因为所求得的结果是浮点数,而不是整数,要注意误差r-l>=EPS,一般EPS1e-6足够了,但有的毒瘤题需要1e-8或1e-10才能过,而且EPS开得太小有可能TLE
这题对所求距离进行二分,听说别的同学对角度进行二分一直过不了。
列出三条方程得到R与所求距离的关系式。
代码:
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
double newl;
double L,C,N;
bool check(double mid)
{
double temp=2*asin((L*0.5)/(L*L/(8*mid)+mid/2))*(L*L/(8*mid)+mid/2);
if(temp>=newl)
return 1;
return 0;
}
int main()
{
while(scanf("%lf%lf%lf",&L,&C,&N)!=EOF)
{
if(L<0&&C<0&&N<0)
break;
newl=(1+C*N)*L;
double l=0,r=L/2,mid;
while(r-l>1e-8)
{
mid=(l+r)/2;
if(check(mid))
r=mid;
else l=mid;
}
printf("%.3f\n",r);
}
return 0;
}
F题:考察二分交互题
这是我做的第一道交互题,题目本身不难,但是与平常所做的题不同的是,角色交换了过来,我问他回答
需要注意的是每次输入后都要fflush(stdout),清空一下,或者用endl自带清空
代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll l=0,r=1e9,mid;
char ch[3];
while(l<=r)
{
mid=(l+r)/2;
cout<<"Q "<<mid<<endl;
cin>>ch;
cout.flush();
if(ch[0]=='>')l=mid+1;
else if(ch[0]=='<')
r=mid;
else if(ch[0]=='=')
return 0;
}
return 0;
}
3.三分
三分与二分的区别在于二分所解决的是一段单调序列,而三分可以用来解决一段既有单调递增又有单调递减的序列。做法与二分差不多,取两个点为L=l+(r-l)/3.0与R=r-(r-l)/3.0
E题:三分模板题,求一个n个二次函数在区间[0,100]的最小值中的最小值
这道题本身不难,但是沙雕的我定义了两次三分点,在刚开始定义一次,在循环里又定义了一次,导致WA了十多次都找不出错误,最后去问ksf,才解决的
直接上代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
double a[10005],b[10005],c[10005];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
}
double l=0,r=1000,lm,rm;
while(r-l>1e-10)
{
lm=l+(r-l)/3.0,rm=r-(r-l)/3.0;
double maxl=a[1]*lm*lm+b[1]*lm+c[1],maxr=a[1]*rm*rm+b[1]*rm+c[1];
for(int i=2;i<=n;i++)
{
maxl=max(a[i]*lm*lm+b[i]*lm+c[i],maxl);
maxr=max(a[i]*rm*rm+b[i]*rm+c[i],maxr);
}
if(maxl<maxr)
{
r=rm;
}
else l=lm;
}
double maxx=a[1]*lm*lm+b[1]*lm+c[1];
for(int i=2;i<=n;i++)
{
maxx=max(a[i]*lm*lm+b[i]*lm+c[i],maxx);
}
printf("%.4lf\n",maxx);
}
return 0;
}