2019年8月训练(壹)二分,三分

二分查找

P1024 一元三次方程求解

题目给出范围[-100,100],同时两根绝对值之差<=1,保证了每一个大小为1的区间里至多有1个解,也就是说当区间的两个端点的函数值异号时区间内一定有一个解,同号时一定没有解。

也就可以二分去做查找。

AC码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

double a,b,c,d;

double f(double x)
{
    return a*x*x*x+b*x*x+c*x+d;
}

int main()
{
    double l,r,x1,x2,mid;
    int cnt=0;
    scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
    for(int i=-100;i<=100;i++)
    {
        l=i;
        r=i+1;
        x1=f(l);
        x2=f(r);
        if(!x1)
        {
            printf("%.2lf ",l);
            cnt++;
        }
        if(x1*x2<0)
        {
            while((r-l)>=0.001)
            {
                mid=(l+r)/2;
                if(f(mid)*f(r)<=0)
                {
                    l=mid;
                }
                else r=mid;
            }
            printf("%.2lf ",r);
            cnt++;
        }
        if(cnt==3) break;
    }
    return 0;
}

这里对以下的一段代码稍稍说明一下

while((r-l)>=0.001)//区间左右距离在可行的范围内
            {
                mid=(l+r)/2;//中间值,也就是二分点
                if(f(mid)*f(r)<=0)//判断条件(根据题意可以进行替换)
                {
                    l=mid;//接着查找右区间[mid,r]
                }
                else r=mid;//接着查找左区间[l,mid]
            }

上面就是二分的核心部分,至于判断条件,根据题意自己写个·bool函数吧

P1182 数列分段 Section II

这道题难得不是二分,而是判断(每道都是这样)。

这里提供一种方法

首先画一条线,也就是分段后和的最大值(一般去mid),也就是这里的now。

然后逐渐减小这个now,只要分段数(cnt)合法。

第二次:now=6,cnt=3.

第三次:now=5,但是cnt=4(不合法),结束。

AC码:

#include<cstdio>
#include<cstring>
#define maxn 100010
#include<algorithm>
using namespace std;

int n,m,a[maxn],now,cnt;

bool pd(int mid)
{
    now=mid;cnt=1;
    for(int i=1;i<=n;i++)
    {
        if(now<a[i])
        {
            cnt++;
            now=mid-a[i];
        }
        else now-=a[i];
    }
    return cnt<=m;
}

int main()
{
    int l=0,r=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        l=max(l,a[i]);
        r+=a[i];
    }
    int mid;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(pd(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    return 0;
}

P2678 跳石头

二分跳跃距离,然后把这个跳跃距离“认为”是最短的跳跃距离,然后去以这个距离为标准移石头。判断这个解是不是可行解。如果这个解是可行解,那么有可能会有比这更优的解,那么我们就去它的右边二分。为什么去右边?答案是,这个区间是递增的 ,而我们求的是最短跳跃距离的最大值,显然再右边的值肯定比左边大,那么我们就有可能找到比这更优的解,直到找不到,那么最后找到的解就有理由认为是区间内最优解。反过来,如果二分到的这个解是一个非法解,我们就不可能再去右边找了。因为性质,右边的解一定全都是非法解。那么我们就应该去左边找解。

如何实现判断呢?可以去模拟这个跳石头的过程。开始你在i(i=0)位置,我在跳下一步的时候去判断我这个当前跳跃的距离,如果这个跳跃距离比二分出来的mid小,那这就是一个不合法的石头,应该移走。为什么?我们二分的是最短跳跃距离,已经是最短了,如果跳跃距离比最短更短岂不是显然不合法,是这样的吧。移走之后要怎么做?先把计数器加上1,再考虑向前跳啊。去看移走之后的下一块石头,再次判断跳过去的距离,如果这次的跳跃距离比最短的长,那么这样跳是完全可以的,我们就跳过去,继续判断,如果跳过去的距离不合法就再拿走,这样不断进行这个操作,直到i = n+1,为啥是n+1?河中间有n块石头,显然终点在n+1处。(这里千万要注意不要把n认为是终点,实际上从n还要跳一步才能到终点)。模拟完这个过程,我们查看计数器的值,这个值代表的含义是我们以mid作为答案需要移走的石头数量,然后判断这个数量 是不是超了就行。如果超了就返回false,不超就返回true。

AC码:

#include<cstdio>
#include<cstring>
#define maxn 500010
#include<algorithm>
using namespace std;

int l,r,n,m,a[maxn],d,ans;

bool pd(int mid)
{
    int tot=0,next=0,now=0;//next下个石头、now当前所在石头
    while(next<n+1)
    {
        next++;
        if(a[next]-a[now]<mid)
        {
            tot++;//拿走石头,再看下一块
        }
        else now=next;//不拿,直接跳过去
    }
    if(tot>m) return false;
    else return true;
}

int main()
{
    scanf("%d%d%d",&d,&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    a[n+1]=d;//n+1,注意
    l=1;r=d;
    int mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(pd(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}

P2985 [USACO10FEB]吃巧克力Chocolate Eating

先二分求出最不开心那天的最大开心值ans

再对ans重新走一遍以记录正确日期(这里大概最难吧)

直接代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 500010
using namespace std;

long long ans,day[maxn],l,r,mid,n,d,h[maxn];

bool eat(long long x)
{
    long long cnt=0,sum=0;
    for(int i=1;i<=d;i++)
    {
        sum=sum>>1;
        while(sum<x)
        {
            sum+=h[++cnt];//达不到x便返回
            if(cnt>n) return false;
            if(x&&x==ans) day[cnt]=i;//if保证仅对ans记录日期
        }
    }
    return true;
}

int main()
{
    scanf("%lld%lld",&n,&d);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&h[i]);
        r+=h[i];
    }
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(eat(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%lld\n",ans);
    eat(ans);
    for(int i=1;i<=n;i++)
    {
        if(day[i]) printf("%lld\n",day[i]);
        else printf("%lld\n",d);//要吃完巧克力,剩下的就直接输出没吃的
    }
    return 0;
}

P1314 聪明的质监员(要被开除的质检员)

在W取0时,所有的区间内的矿石都可以选上,

而在W大于最大的质量时,所有的矿石都选不上。

然后简单算一下就发现:

W越大,矿石选的越少,W越小,矿石选的越多。

所以w↑,y↓

当Y>s 时,需要增大WW来减小YY,从而|Y-s|变小;

当Y==s时,∣Y−s∣==0;

当Y<s时,需要减小W来增大YY,从而|Y-s|变大;

之后用前缀和去算区间val和,这样快。

AC码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 200010
#define ll long long
using namespace std;

ll ans,s;
int n,m,w[maxn],v[maxn],l[maxn],r[maxn],maxx,minn=2147483647;
ll qaqn[maxn],qaqv[maxn],tot,sum;

bool pd(int ww)
{
    tot=0,sum=0;
    memset(qaqn,0,sizeof(qaqn));
    memset(qaqv,0,sizeof(qaqv));
    for(int i=1;i<=n;i++)
    {
        if(w[i]>=ww)
        {
            qaqn[i]=qaqn[i-1]+1;
            qaqv[i]=qaqv[i-1]+v[i];
        }
        else
        {
            qaqn[i]=qaqn[i-1];
            qaqv[i]=qaqv[i-1];
        }
    }
    for(int i=1;i<=m;i++)
    {
        tot+=(qaqn[r[i]]-qaqn[l[i]-1])*(qaqv[r[i]]-qaqv[l[i]-1]);
    }
    sum+=llabs(tot-s);
    if(tot>s) return true;
    else return false;
}

int main()
{
    scanf("%d%d%lld",&n,&m,&s);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&w[i],&v[i]);
        maxx=max(maxx,w[i]);
        minn=min(minn,w[i]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l[i],&r[i]);
    }
    int ql=minn-1,qr=maxx+2,mid;
    ll ans=0x3f3f3f3f3f3f3f3f;
    while(ql<=qr)
    {
        mid=(ql+qr)>>1;
        if(pd(mid)) ql=mid+1;
        else qr=mid-1;
        ans=min(sum,ans);
    }
    printf("%lld",ans);
    return 0;
}

P2571 [SCOI2010]传送带

三分套三分,把线段ab三分寻找转折点后从转折点跑向线段cd,然后再在线段cd上三分寻找抵达地点跑向点d。

之后我陷入了一脸懵的状态,三分与a,c的距离,但是这个距离根本无法确定该点的坐标。

之后看了大佬的题解后...啥!还可以三分比例!

然后

不是太理解。。。讲不出来,只好把那位dalao的话引用过来了

我们现在已知线段AC,假设现在在AC上已找到一点E,我们要去计算E与另一条线段的距离,这个E的坐标怎么求呢?

以AC为斜边,作一个Rt△ABC。作ED⊥CB,此时我们可以发现,△AED和△ACB是相似三角形

所以AE和AC有一定的比值,即AC/AE?=k(0≤k≤1)。

所以我们可以直接三分k,然后就可以求出F的坐标了。

AC码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 10010
#define eps 1e-7
using namespace std;

double ax,ay,bx,by,cx,cy,dx,dy,P,Q,R;//其实可以用结构体来存四个点的坐标,这样后面的函数更方便写

double dis(double x,double y,double xx,double yy)//又臭又长
{
    return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}

double ctx(double x,double xx,double k)
{
    double qaq;
    qaq=(xx-x)*k+x;
    return qaq;
}

double cty(double y,double yy,double k)
{
    double qwq;
    qwq=(yy-y)*k+y;
    return qwq;
}

double expd(double x,double y)
{
    double nx1=ctx(ax,bx,x),ny1=cty(ay,by,x),nx2=ctx(cx,dx,y),ny2=cty(cy,dy,y);//这就是不用结构体的下场
    return dis(ax,ay,nx1,ny1)/P+dis(nx1,ny1,nx2,ny2)/R+dis(nx2,ny2,dx,dy)/Q;
}

double pd(double x)
{
    double l=0.0,r=1.0;
    while(r-l>=eps)
    {
        double mid=l+(r-l)/3.0,mmid=r-(r-l)/3.0;
        if(expd(x,mid)>expd(x,mmid)) l=mid;
        else r=mmid;
    }
    return expd(x,l);
}

int main()
{
    scanf("%lf%lf%lf%lf",&ax,&ay,&bx,&by);
    scanf("%lf%lf%lf%lf",&cx,&cy,&dx,&dy);
    scanf("%lf%lf%lf",&P,&Q,&R);
    double l=0.0,r=1.0;
    while(r-l>=eps)
    {
        double mid=l+(r-l)/3.0,mmid=r-(r-l)/3.0;
        if(pd(mid)>pd(mmid)) l=mid;
        else r=mmid;
    }
    printf("%.2lf",pd(l));
    return 0;
}

2019-08-01 23:42:18

原文地址:https://www.cnblogs.com/plzplz/p/11286134.html

时间: 2024-10-09 09:16:54

2019年8月训练(壹)二分,三分的相关文章

2019年9月训练(壹)数位DP (HDU 2089)

开学之后完全没时间写博客.... HDU 2089 不要62(vjudge) 数位DP 思路: 题目给出区间[n,m] ,找出不含4或62的数的个数 用一个简单的差分:先求0~m+1的个数,再减去0~n的个数. 但问题依旧不简单,再次简化为求0~i位数中不含4或62的数的个数. i=1 //0~9中 i=2 //0~99中 i=3 //0~999中 ...... dp[i][0] //0~i位数中的吉利数 dp[i][1] //0~i位数中以2打头的吉利数 dp[i][2] //0~i位数中的非

2019年7月训练(壹)

2019-07-25 luogu P3627 [APIO2009]抢掠计划 卡了三个小时,看了题解才作出来的(菜) 前驱知识: 壹~邻接表存储/遍历 贰~SPFA跑最长路(<改>就行了) 叄~Tarjan缩点 壹.邻接表储存 两个,add存无边权,未缩点:build有边权,已缩点. void add(int u,int v) { cnt++; e[cnt].to=v; e[cnt].next=head[u]; head[u]=cnt; } void build(int u,int v,int

2019年1月训练记录(更新ing)

前言 时光飞逝,转眼间,便到了\(2019\)年. 这一年里,还是要继续努力吧,好好学习一些新的算法. 当然,还是要好好准备一下期末考试啦... ... \(Jan\ 1st\) 原文地址:https://www.cnblogs.com/chenxiaoran666/p/2019Jan.html

2019年7月训练(陆)

模板:luogo P3379 [模板]最近公共祖先(LCA) 今天讲的时候有点跑神,现在卑微地来补习(菜) LCA指的是最近公共祖先(Least Common Ancestors). 最简单的算法无疑是从两个点一个个往上走,出现的第一个两个点都走过的点即为两点的LCA. 但是时间很长. 所以起用倍增,倍增的作用就是将两点上升所需的复杂度减低. 大致流程为:将deep不同的两个点跳到同一层,再跳到deep[lca-1]的那层,再向上跳一层就是lca了. 加速跳的方法就是每次向上跳的层数为2的i次方

IT帮2019年2月线下活动【定义工作,解读自我】之站桩练习

2019年2月IT帮线下活动[定义工作,解读自我] 昨天的活动收获很大,全面的总结周老师会另写一篇来帮助大家回顾.我想说一下其中最打动我的一句话:“只有你能决定你有多优秀!” “工作中把自己当成企业家,把你的工作当成创业,公司给你提供了可以协作的团队.场所”.责任感程度不同,就有不同的结果.当你认为是别人派给你的活儿时,你也许可以去尽心完成,也或者会放弃.当你把事情当成你对别人的承诺.非你完成不可时,你一定会完成,甚至会有创新,想法设法地做得更好,这就是创造. 站桩练习 活动期间,周老师推荐我带

周记 - 2019年11月03日

2019年11月05日 2019年徐州区域赛结束了.封榜前3题铜牌前部,封榜后最后27分钟罚了4次通过E题.虽然4题罚时爆炸,不过万幸得了银牌后部.目前看应该还会再参加一年的,这个博客会不断更新记录最后一年参加比赛的学习进度(以及最后两年本科的其他事情),今年的目标是做一个真正的全能选手,首先希望在寒假结束前把Codeforces打到橙色(2100+),证明自己思维还行吧. 既然想做全能选手,每个专题都要学到省选级别吧,学习的分类就参照OI-Wiki的分类,题单的话,模板题从洛谷找,思维题从Co

蔡康永的说话之道——2019年12月15日

.bodyContainer { font-family: Arial, Helvetica, sans-serif; text-align: center; padding-left: 32px; padding-right: 32px; } .notebookFor { font-size: 18px; font-weight: 700; text-align: center; color: rgb(119, 119, 119); margin: 24px 0px 0px; padding:

Toxophily-数论以及二分三分

G - Toxophily Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 2298 Description The recreation center of WHU ACM Team has indoor billiards, Ping Pang, chess and bridge, toxophily, deluxe ballroom

西安活动 | 2019年1月13号 &quot;拥抱开源, 又见.NET&quot; 线下交流活动报名进行中

随着.NET Core的发布和开源,.NET又重新回到人们的视野..NET Core的下个3.0即将release,加入非常多的新功能,越来越拥抱变化,DevOps和Microservice的最佳实践已经在.NET Core落地,比如 Ocelot网关.Grpc+Consul 服务注册发现.Apworks CQRS实现.Xigadee 微服务工具库.脚手架. 西安.NET社区组织发起了此次“拥抱变化, 又见.NET”线下交流活动,邀请了三位资深.NET开发者作为分享讲师,他们将从架构.原理.语言