2018专题集训一 第一部分(二分,三分,尺取)

[题目链接] : 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值