二分查找题

添加链接描述例如二分中如何取到最小时间或者最小值,一般要考虑到用二分。
二分模板

    int l=1,r=n;
    while(l<r)
    {
         int mid=(l+r)>>1;
         if(check(mid)) r=mid;
         else l=mid+1;
     }
   //两个模板都可以用 只不过有的时候下面才能出正确答案
    int l=1,r=n;
    while(l<=r)
    {
         int mid=(l+r)>>1;
         if(check(mid)) r=mid-1;
         else l=mid+1;
     }

check函数用于判断是否符合所需的答案
P1843 奶牛晒衣服

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=5e5+5;
ll n,m,len=0,tot=0,a[N],x,y;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
bool check(ll k)
{
    ll m=k;
    fr(i,n)
        if(m*x<a[i])
            k-=ceil((double)(a[i]-m*x)/y); ///找到让所有衣服都能干的时间
    return k>=0;
}
int main()
{
    n=read();x=read();y=read();
    fr(i,n) a[i]=read();
    ll l=1,r=1e8;
    while(l<r)
    {
        ll mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<l;
    return 0;
}

又是看题解 写题的一天
P2390 地标访问 这是一道二分枚举题目,因为路标是线性分布的所以没有必要去dfs搜索(在这上面想了很长时间没有头绪),有以下三种情况判断:

  1. 一直向右边走
  2. 一直往左边走
  3. 先往一边走然后回头走另一边
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=2e5+5;
ll n,m,len,tot=0,x,y,a[N],sum=0,b[N],pos;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
int check(int x)
{
     for(int i=x;i<=n;i++)
     {
         int l=i-x+1;
         if(a[l]>=0)
            if(a[i]<=m) return 1;
         if(a[i]<=0)
            if(-a[l]<=m) return 1;
         if(a[l]<=0&&a[i]>=0)
          if (min(-a[l],a[i])+a[i]-a[l]<=m) return 1;
     }
     return 0;
}
int main()
{
     m=read(),n=read();
     fr(i,n) a[i]=read();
     sort(a+1,a+n+1);
     ll l=-1,r=n+1;
     while(l+1<r)
     {
         ll mid=(l+r)>>1;
         if(check(mid)) l=mid;
         else r=mid;
     }
     cout<<l<<endl;
    return 0;
}

0/1分数规划
1)知识点
这道题就是一道简单的01分数规划,再运用二分。

2)看题目
这道题要求单位价值(题目中的总价值/总花费),我们假设单位价值为x,我们就可以知道,x = Σv / Σc。

3)算法分析
二分是本题的答案:假设一个数L 如果有若干对 ai/bi 大于L则说明L比最大值要小,而如果对于所有的 ai/bi 都要比L要小 则L比最大值要大 。由此得出L具有单调性通过二分:得到L 选出K对ai和bi使得 Σai - Σbi * L > 0即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=2e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N];
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
bool check(double x)
{
    double c[N],sum=0;
    fr(i,n) c[i]=a[i]-b[i]*x;
    sort(c+1,c+n+1,greater<double>() );
    fr(i,m) sum+=c[i];
    return sum>=0;
}
int main()
{
    int t=read();
    while(t--)
    {
       n=read(),m=read();
       fr(i,n) b[i]=read(),a[i]=read();
       double l=0,r=INF;
       for(int i=1;i<=100;i++) ///保证高精度
       {
           double mid=(l+r)/2;
           if(check(mid)) l=mid;
           else r=mid;
       }
       cout<<(int)l<<endl;
    }
    return 0;
}

P2678 [NOIP2015 提高组] 跳石头
本题思路大致如下:枚举 a[i]-a[j]<mid 则ans加一 否则j=i

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N],len;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
bool check(int x)
{
   int sum=0,j=0;
   fr(i,n+1)
      if(a[i]-a[j]<x) sum++;  ///线性枚举
      else j=i;
   return sum<=m;
}
int main()
{
       len=read(),n=read(),m=read();
       fr(i,n) a[i]=read();
       sort(a+1,a+n+1);
       a[n+1]=len;
       int l=0,r=INF;
       while(l<=r)
       {
           int mid=(l+r)/2;
           if(check(mid)) l=mid+1;
           else r=mid-1;
       }
       cout<<l-1<<endl;
    return 0;
}

P3853 [TJOI2007]路标设置
一道思路简单的二分题:

  1. 二分出最大距离x 判断当前位置路标位置 j 是否和后一个路标距离小于x
  2. 若距离大于x则在 j+x 的地方放置路标 循环直到距离小于x
  3. 若距离小于x则到下一个路标的位置
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N],len;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
bool check(int x)
{
   int sum=0,j=0;
   fr(i,n)
     {
          while(j+x<a[i]) j+=x,sum++;
          j=a[i];
     }
   return sum<=m;
}
int main()
{
       len=read(),n=read(),m=read();
       fr(i,n) a[i]=read();
       int l=0,r=INF;
       while(l<r)
       {
           int mid=(l+r)/2;
           if(check(mid)) r=mid;
           else l=mid+1;
       }
       cout<<l<<endl;
    return 0;
}

P1281 书的复制
这题的思路是没有问题的,只是根据题目要求,第一个人写的尽量少那么就从n到1枚举。在输出答案之前应该先进行判断最好再去输出,因为我便判断边记录答案,最好导致答案出了问题。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
int n,m,a[N],cnt=0,t=0;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
struct node
{
    int l,r;
}b[N];
bool check(int x)
{
    int sum=1,t=x-a[n];
    for(int i=n-1;i>=1;i--)
    {
        if(a[i]<=t) t-=a[i];
        else
        {
            t=x-a[i];
            sum++;
        }
    }
    return sum<=m;    ///先判断再去记录答案
}
void print(int x)
{
    /*cout<<x<<endl;*/
    int k=x-a[n],cnt=0;
    b[++cnt].r=n;
    for(int i=n-1;i>=1;i--)
    {
        if(a[i]<=k) k-=a[i];
        else
        {
            k=x-a[i];
            b[cnt].l=i+1;
            b[++cnt].r=i;
        }
    }
    b[m].l=1;
    for(int i=m;i>=1;i--) cout<<b[i].l<<" "<<b[i].r<<endl;
}
int main()
{
       n=read(),m=read();
       fr(i,n) a[i]=read();
       ll l=0,r=INF;
       while(l<=r)
       {
           ll mid=(l+r)/2;
           if(check(mid)) r=mid-1;
           else l=mid+1;
       }
       print(l);
    return 0;
}

P1083 [NOIP2012 提高组] 借教室
思路:二分订单数量,差分若当前天数的教室小于0则r=mid(r往更小的范围寻找),否则l=mid;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=1e6+5,INF=0x3f3f3f3f;
ll n,m,cnt=0,t=0,a[N],c[N],flag=0;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
struct node
{
    ll l,r;
    int d;
}b[N];
int check(int x)
{
      memset(c,0,sizeof(c));
      fr(i,x) c[b[i].l]-=b[i].d,c[b[i].r+1]+=b[i].d;
      fr(i,n)
      {
          c[i]+=c[i-1];
          int t=a[i]+c[i];
          if(t<0)
          {
              flag=1;
              return 1;
          }
      }
      return 0;
}
int main()
{
       n=read(),m=read();
       fr(i,n) a[i]=read();
       fr(i,m) b[i].d=read(),b[i].l=read(),b[i].r=read();
       int l=1,r=m;
        while(l<r)
       {
           int mid=(l+r)>>1;
           if(check(mid))  r=mid;
           else l=mid+1;
       }
       if(flag)
     {
       cout<<-1<<endl;
       cout<<l<<endl;
      }
       else
          cout<<0<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值