二分专题题目讲解附代码.docx

上传人:b****6 文档编号:8177573 上传时间:2023-01-29 格式:DOCX 页数:25 大小:27.72KB
下载 相关 举报
二分专题题目讲解附代码.docx_第1页
第1页 / 共25页
二分专题题目讲解附代码.docx_第2页
第2页 / 共25页
二分专题题目讲解附代码.docx_第3页
第3页 / 共25页
二分专题题目讲解附代码.docx_第4页
第4页 / 共25页
二分专题题目讲解附代码.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

二分专题题目讲解附代码.docx

《二分专题题目讲解附代码.docx》由会员分享,可在线阅读,更多相关《二分专题题目讲解附代码.docx(25页珍藏版)》请在冰豆网上搜索。

二分专题题目讲解附代码.docx

二分专题题目讲解附代码

以下是个人整理出的有关二分专题的题目,代码有些头文件已删去,为了减少代码长度(无大碍),希望对大家学习有所帮助!

智商问题

描述Description

某个同学又有很多小姊妹了

他喜欢聪明的小姊妹 所以经常用神奇的函数来估算小姊妹的智商

他得出了自己所有小姊妹的智商

小姊妹的智商都是非负整数

但是这个同学看到别的同学的小姊妹

也喜欢用神奇的函数估算一下

然后看看这个小姊妹在自己的小姊妹群体中排在第几位...

输入格式InputFormat

第一行一个整数N 代表小姊妹的个数

第二行N个整数 代表这位同学N个小姊妹的智商

接下来若干行 每行一个整数

代表这位同学看中的别人的小姊妹的智商

0<=智商<=2^31-1

0<=N<=1000000

输出格式OutputFormat

输出若干行

每行一个整数 回答新的小姊妹

在原来小姊妹中智商的排名

样例输入 

5

12345

1

2

3

4

5

样例输出 

1

2

3

4

5

 主要矛盾就是那个0<=n<=1000000,没有这个一切都好说,但是如果没有这个,就没有写的价值了。

    思想其实很简单,就是排个序,再二分。

    二分,只要计算log2(n)次,即可找出答案。

而log2(1000000)≈20,实在是高效得逆天。

关于二分的讲解,在以前的日志《最长不下降子序列》中有讲。

我奉行动态规划的思想,同样的事不做第二遍。

    而现在,最主要的矛盾是排序。

由于我怕出题人卡快排,于是就用堆排,而堆排的时间复杂度是n*log2(n)(又是二分的思想),很稳定,卡不到。

    虽说这道题在TYVJ上属于高级数据结构,但是其实它也没有用什么数据结构。

不过它的难度为2还是很合适。

#include

#include

constintmaxn=1000000;

intn,k;

inta[maxn+10],b[maxn+10];

 

voidinsert()

{

     inti=++k;

     while(i>1&&a[i]>1])

     {

         intnow=a[i];  a[i]=a[i>>1];a[i>>1]=now;

         i=i>>1;

           }

 }

 

voidrepair()

{

     a[1]=a[k--];

     inti=1,j=2;

     if(j+1<=k&&a[j+1]

    

     while(j<=k&&a[j]

     {

         intnow=a[i];a[i]=a[j];a[j]=now;

         i=j;  j=j<<1;

         if(j+1<=k&&a[j+1]

           }

 }

 

voidread()

{

     freopen("sister.in","r",stdin);

     freopen("sister.out","w",stdout);

     scanf("%d",&n);

     for(inti=1;i<=n;i++)

     {

         scanf("%d",&a[i]);

         insert();

     }

    

     for(inti=1;i<=n;i++)

     {

         b[i]=a[1];

         repair();

     }

 }

 

voidwork()

{

     intnow;

     while

(1)

     {

         now=-1;

         scanf("%d",&now);

         if(now==-1)break;

         intl=1,r=n,m;

         while(l<=r)

         {

             intmid=(l+r)>>1;

             if(now<=b[mid])

             {

                 r=mid-1;

                 m=mid;

                             }

             else 

             {

                 l=mid+1;

                 m=mid;

                   }

               }

         if(now<=b[m])printf("%d\n",m);

         else  printf("%d\n",m+1);

           }

 }

 

intmain()

{

    read();

    work();

return0;

}

   二分很简单,真的,不说了。

我的程序是模块化的,可读性应该还可以。

]教主的花园

【问题背景】

LHX教主最近总困扰于前来膜拜他的人太多了,所以他给他的花园加上了一道屏障。

 

【问题描述】

可以把教主的花园附近区域抽像成一个正方形网格组成的网络,每个网格都对应了一个坐标(均为整数,有可能为负),若两个网格(x1,y1),(x2,y2)有|x1–x2|+|y1–y2|=1,则说这两个网格是相邻的,否则不是相邻的。

教主在y=0处整条直线上的网格设置了一道屏障,即所有坐标为(x,0)的网格。

当然,他还要解决他自己与内部人员的进出问题,这样教主设置了N个入口a1,a2,…,aN可供进出,即对于y=0上的所有网格,只有(a1,0),(a2,0),……,(aN,0)可以通过,之外的所有纵坐标为0的网格均不能通过,而对于(x,y)有y不为0的网格可以认为是随意通过的。

现在教主想知道,给定M个点对(x1,y1),(x2,y2),并且这些点均不在屏障上,询问从一个点走到另一个点最短距离是多少,每次只能从一个格子走到相邻的格子。

 

【输入格式】

输入的第1行为一个正整数N,为屏障上入口的个数。

第2行有N个整数,a1,a2,…,aN,之间用空格隔开,为这N个入口的横坐标。

第3行为一个正整数M,表示了M个询问。

接下来M行,每行4个整数x1,y1,x2,y2,有y1与y2均不等于0,表示了一个询问从(x1,y1)到(x2,y2)的最短路。

 

【输出格式】

输出共包含m行,第i行对于第i个询问输出从(x1,y1)到(x2,y2)的最短路距离是多少。

 

【样例输入】

2

2-1

2

010-1

1122

 

【样例输出】

4

2

 

【数据规模】

对于20%的数据,有n,m≤10,ai,xi,yi绝对值不超过100;

对于40%的数据,有n,m≤100,ai,xi,yi绝对值不超过1000;

对于60%的数据,有n,m≤1000,ai,xi,yi绝对值不超过100000;

对于100%的数据,有n,m≤100000,ai,xi,yi绝对值不超过100000000。

 这道题我一看就知道就知道了是用二分来查找,搞不懂为什么学长们就没有一个能得到60分以上。

    不多说了,这道题水。

    纵坐标直接绝对值相减就是了。

    如果两个点都是在纵坐标的正半轴或是负半轴,那根本不用通过路口,横座标也直接相减就是了。

    如果需要跨越屏障,那么找一个a,让|x1-a|+|x2-a|最小就是了。

也就是找最近的那几个,用二分就是了。

    本来刚开始还想过直接用multiset<>的,但是STL的调用时间我早就领教过了,哆嗦了一下,还是用的手打二分。

 

intn;

constintmaxn=100000;

inta[maxn+10];

 

inlineintfind1(intx)

{

    intl=1,r=n,m;

    while(l<=r)

    {

        intmid=(l+r)>>1;

        if(a[mid]<=x)

        {

            l=mid+1;

            m=mid;

                      }

        else  r=mid-1;

          }

    returna[m];

}

 

inlineintfind2(intx)

{

    intl=1,r=n,m;

    while(l<=r)

    {

        intmid=(l+r)>>1;

        if(a[mid]>=x)

        {

            r=mid-1;

            m=mid;

                      }

        else  l=mid+1;

          }

    returna[m];

}

 

inlinevoidwork()

{

     intx1,x2,y1,y2;

     scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

     if(x1>x2)swap(x1,x2);

    

     if((y1>0&&y2>0)||(y1<0&&y2<0))

     {

         printf("%d\n",abs(y2-y1)+abs(x2-x1));

         return;

               }

     if(x1<=a[1])

     {

         printf("%d\n",abs(y2-y1)+abs(x1-a[1])+abs(x2-a[1]));

         return;

                  }

     if(x2>=a[n])

     {

         printf("%d\n",abs(y2-y1)+abs(x1-a[n])+abs(x2-a[n]));

         return;

                  }

     if(find2(x1)<=x2)

     {

         printf("%d\n",abs(y2-y1)+abs(x2-x1));

         return;

                         }

     inta1=find1(x1),a2=find2(x2);

     intans1=abs(y1-y2)+abs(x1-a1)+abs(x2-a1),

         ans2=abs(y1-y2)+abs(x1-a2)+abs(x2-a2);

     printf("%d\n",min(ans1,ans2));

 }

 

intmain()

{

    freopen("p1.in","r",stdin);

    freopen("p1.out","w",stdout);

    scanf("%d",&n);

    for(inti=1;i<=n;i++)

        scanf("%d",&a[i]);

    sort(a+1,a+n+1);

   

    intm;

    scanf("%d",&m);

    for(inti=0;i

        work();

    fclose(stdin);

    fclose(stdout);

return0;

}

软件software

 【题目描述】

    一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。

一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。

写一个程序,求出公司最早能在什么时候交付软件。

【输入格式】

    输入文件第一行包含两个由空格隔开的整数n和m,其中1≤n≤100,1≤m≤100。

接下来的n行每行包畲两个用空格隔开的整数d1和d2:

,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数,其中l≤d1,d2≤100。

【输出格式】

    输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。

【样例】

 software.in

 320

 11

 24

 16

 software.out

 18

【样例解释】

    最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。

如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。

 

    这个,一开始用的是三维的动规,用f[i][j][k]表示前i个人,第一种软件生产了j个,第二种生产了k个,花的最少时间。

转移方程为:

f[i][j][k]=min(max(f[i-1][x][y],(j-x)*d1+(k-y)*d2))。

但这个方程的时间复杂度是Ο(n^5)的,只能过6组数据。

    然后看题解,是先二分答案,再动规。

设当前二分的中点是mid,然后用f[i][j]表示前i个人,生产第一种软件j个,然后最多可以生产多少个第二种软件。

f[i][j]=max(f[i-1][j-k]+(mid-k*d1)/d2)。

k=min(mid/d1,j)。

    二分就可以了。

把二分的右界设成0x7ffffff,也很快就过了。

    这种类型的题要多留心,应该一眼就看出它是二分,这样才好。

    Code:

intn,m;

constintmaxn=100;

intf[2][maxn+10],a[maxn+10],b[maxn+10];

 

booldp(intmid)

{

     memset(f,-1,sizeof(f));

     intnow=0,pas=1;

     f[now][0]=0;

    

     for(inti=0;i

     {

         swap(pas,now);

         for(intj=0;j<=m;j++)

             for(intk=0;k<=min(mid/a[i],j);k++)

                 if(f[pas][j-k]!

=-1)

                 {

                     inttmp=(mid-k*a[i])/b[i];

                     f[now][j]=max(f[now][j],f[pas][j-k]+tmp);

                                 }

     }

    

     return(f[now][m]>=m?

1:

0);

 }

 

intmain()

{

    freopen("software.in","r",stdin);

    freopen("software.out","w",stdout);

    scanf("%d%d",&n,&m);

    for(inti=0;i

        scanf("%d%d",&a[i],&b[i]);

       

    intl=0,r=0x7ffffff,ans;

    while(l<=r)

    {

        intmid=(l+r)>>1;

        if(dp(mid))

        {

            r=mid-1;

            ans=mid;

                    }

        else  l=mid+1;

          }

   

    printf("%d\n",ans);

    fclose(stdin);

    fclose(stdout);

return0;

}

二分/最短路]收费站cost

【题目描述】

  在某个遥远的国家里,有n个城市。

编号为1,2,3,……,n。

  这个国家的政府修建了m条双向的公路。

每条公路连接着两个城市。

沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。

  开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。

所有的收费站都在城市中,在城市间的公路上没有任何的收费站。

  小徐现在要开车从城市u到城市v(1<=u,v<=n)。

她的车最多可以装下s升的汽油。

在出发的时候,车的油箱是满的,并且她在路上不想加油。

  在路上,每经过一个城市,她要交一定的费用。

如果她某次交的费用比较多,她的心情就会变得很糟。

所以她想知道,在她能到达目的地的前提下,她交的费用中最多的一次最少是多少。

这个问题对于她来说太难了,于是她找到了聪明的你,你能帮帮她吗?

【输入格式】

  第一行5个正整数,n,m,u,v,s。

分别表示有n个城市,m条公路,从城市u到城市v,车的油箱的容量为s升。

  接下来有n行,每行1个正整数,fi。

表示经过城市i,需要交费fi元。

  再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=n)。

表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,需要用ci升汽油。

【输出格式】

  仅一个整数,表示小徐交费最多的一次的最小值。

  如果她无法到达城市v,输出-1。

【输入样例1】

44238

8

5

6

10

212

241

134

343

【输出样例1】

8

【输入样例2】

44233

8

5

6

10

212

241

134

343

【输出样例2】

-1

【数据范围】

    对于60%的数据,满足n<=200,m<=10000,s<=200

  对于100%的数据,满足n<=10000,m<=50000,s<=1000000000

  对于100%的数据,满足ci<=1000000000,fi<=1000000000,可能有两条边连接着相同的城市。

   

    去年5月刚学图论时完对这道题完全没有概念,被虐得瓜兮兮的。

    现在一看就知道要二分。

只是还是有一组超时了,没办法了。

    二分收费,然后用spfa,凡是f[i]小于等于当前收费的,就进行松弛,然后看dis[v],如果小于等于s,即可行。

    Code:

 

intn,m,u,v,s;

constintmaxn=10000;

structpoint

{

       intx,l;

       point*next;

       }*p[maxn+10];

intf[maxn+10],dis[maxn+10],a[maxn+10];

boolhash[maxn+10];

intq[5000000];

 

voidinsert(intu,intv,intl)

{

     point*now=newpoint;

     now->x=v;  now->l=l;

     now->next=p[u];  p[u]=now;

 }

 

voidread()

{

     freopen("cost.in","r",stdin);

     freopen("cost.out","w",stdout);

     scanf("%d%d%d%d%d",&n,&m,&u,&v,&s);

     for(inti=1;i<=n;i++)

         scanf("%d",&f[i]),a[i]=f[i];

     while(m--)

     {

         inta,b,c;

         scanf("%d%d%d",&a,&b,&c);

         insert(a,b,c);

         insert(b,a,c);

           }

     sort(a+1,a+n+1);

 }

 

boolspfa(inta)

{

     memset(dis,0x3f,sizeof(dis));

     memset(hash,0,sizeof(hash));

     if(f[u]>a)return0;

     dis[u]=0;  hash[u]=1;

     intl=0,r=0;

     q[r++]=u;

    

     while(l

     {

         intx=q[l++];

         hash[x]=0;

        

         for(point*i=p[x];i;i=i->next)

             if(f[i->x]<=a&&dis[i->x]>dis[x]+i->l)

             {

                 dis[i->x]=dis[x]+i->l;

                 if(!

hash[i->x])

                 {

                     hash[i->x]=1;

                     q[r++]=i->x;

                                 }

                            }

           }

     return(dis[v]<=s?

1:

0);

 }

 

voidwork()

{

     if(!

spfa(a[n]))  printf("%d\n",-1);

     else

     {

         intl=1,r=n,m

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1