二分法/二分思想(二分答案法)

解题方法

二分答案法

适用题型:

求可行解的最大值或最小值问题

解题步骤:

1.确定答案的最大值和最小值

2.判断二分所得值是否满足条件

3.可行解必须具有单调性(当k可行时,k+1可行或者k-1可行)

注意事项:

每次都要确保right和left有一个被改变。

模板:

//可行解最大值问题
int l=minn,r=maxn;
int ans=-1;
while(l<=r)
{
  int mid=(l+r)>>1;
  if(judge(mid))
  {
      ans=mid;
      l=mid+1;
  }
  else
    r=mid-1;
}

问题:

Description

还记得我们新生赛上的这题Averyboy的筷子这题吗?众所周知,Averyboy是一个非常的男孩,既然是一个非常的男孩,那么他就会有许多奇葩的爱好。比如收藏筷子。现在美美旸有n双筷子,编号为1-n,同一双筷子编号相同,美美旸把这些筷子放成一排。现在美美旸对av-boy说,如果你能解决我给你出的一个问题,这些筷子都归你。美美旸的问题是:把这些筷子通过一些操作,使得最后这些筷子是同一双的一定相连。每次的操作是,你可以从这2n支筷子中任意取出一支,剩下的筷子自动合并在一起,然后把这支筷子插入任意一个位置,包括两端。(操作次数无限制。)为了刁难av-boy,美美旸故意定了一个条件,每次只能取出编号大于等于x的筷子。现在你的问题是找出最大的x(1 <= x <= n),使得av-boy一定能顺利拿到筷子。

Input

第一行一个数T,表示测试数据的组数.

接下来T组测试数据。(T <= 5)

每组测试数据的一行为一个数正整数n,代表美美旸的n双筷子。

接下来有2n个数,代表这n双筷子的起始位置。(1<= n <= 100000)

Output

每组测试数据输出一个数,表示最大的x,使得av-boy能够拿到美美旸的筷子。

Sample Input

3
2
1 2 1 2
3
1 3 1 2 2 3
4
1 1 2 2 3 3 4 4

Sample Output

2
3
4
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
typedef long long ll;
#define MAXN 200010
ll a[MAXN];
ll N;
bool judge(int x)
{
   ll b[MAXN];
   memset(b,0,sizeof b);
   int p=0;
   for(int i=1;i<=2*N;i++)
   {
       if(a[i]>=x)
         continue;
           if(!b[a[i]])
           {
               b[a[i]]=++p;
           }
           else
           {
               if(b[a[i]]!=p)
               {
                  //cout<<a[i]<<"  "<<p<<"fffffff "<<b[a[i]]<<endl;
                   return false;
               }
 
               else
               {
                p++;
                continue;
               }
           }
       }
 
   //cout<<x<<"ddddddd"<<endl;
   return true;
}
 
int main()
{
    ios_base::sync_with_stdio(0);
    ll T;
    cin>>T;
    while(T--)
    {
      cin>>N;
      for(ll i=1;i<=2*N;i++)
        cin>>a[i];
      ll l=1,r=N;
      ll ans=-1;
      while(l<=r)
      {
        int mid=(l+r)>>1;
          if(judge(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
      }
    cout<<ans<<endl;
    }
}

Description

为了检验你上午有没有好好听我讲课,于是有了这题。现在有N种物品,每一种物品有一个基础价值a[i],当买k件物品时,第i件物品的最终价值为a[i] + k * i,现在给你S元,你最多能买多少件物品.

Input

第一行为一个正整数T,代表测试数据的组数(T <= 5)
接下来T组测试数据,每组测试数据第一行为两个整数N, S,分别代表物品的总数和给你的S元(N <= 1e5, S <= 1e18)
接下来一行N个正整数,代表N种物品的基础价值a[i](0 <= a[i] <= 1e6)

Output

对于每组测试数据,输出一个整数,代表你最多能买到多少个物品,如果一件物品也买不起,输出0

Sample Input

2
3 21
1 1 1
3 20
1 1 1

Sample Output

3
2

HINT

第一组测试样例,如果买三件物品一共花费1 + 1 * 3 + 1 + 2 * 3 + 1 + 3 * 3 = 21刚好等于S,所以最多买三件物品。第二组测试数据当然是只能买两件物品啊

#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
typedef long long ll;
#define MAXN 100010
ll a[MAXN];
ll b[MAXN];
ll N;
ll s;
bool judge(int x)
{
  for(ll i=1;i<=N;i++)
  {
    b[i] = a[i] + i * x;
  }
  sort(b+1,b+N+1);
  ll sum = 0;
  for(ll i=1;i<=x;i++)
  {
     sum+=b[i];
  }
  if(sum<=s)
  {
    return true;
  }
  else return false;
}
 
int main()
{
    ll T;
    cin>>T;
    while(T--)
    {
      cin>>N>>s;
      for(ll i=1;i<=N;i++)
        cin>>a[i];
      ll l=1,r=N;
      ll ans=-1;
      while(l<=r)
      {
        int mid=(l+r)>>1;
          if(judge(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
      }
    cout<<ans<<endl;
    }
}

众所周知,averyboy是一个非常男孩。天外天给你他一个问题。问题如下:给你一个长度为N的序列a[1]~a[N]和一个整数sum,我们称一个区间为averyboynb区间,当且仅当这个区间的所有数的和不超过sum。现在你需要找出一个区间和最大的averyboynb区间。

Input

第一行一个整数T,代表测试数据的组数(T <= 5)
接下来T组测试数据。
每组测试数据的第一行为两个数N, sum(N <= 1e5, sum <= 1e18)分别代表序列的长度和上述的sum。
接下来一行N个整数代表序列a[i](1 <= a[i] <= 1e6)

Output

对于每组测试数据,输出一个整数,代表区间和最大的averyboynb区间的区间和,如果不存在averyboynb区间直接输出averyboynb.

Sample Input

3
3 4
1 2 3
3 6
2 2 3
3 1
2 2 2

Sample Output

3
5
averyboynb 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll s;
#define MAXN 100010
ll a[MAXN];
ll Sum[MAXN];
long long f[MAXN];
int main()
{
    ios_base::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--)
    {
        int N;
        cin>>N>>s;
        Sum[0]=0;
        for(int i=1;i<=N;i++)
        {
          cin>>a[i];
          Sum[i]=Sum[i-1]+a[i];
          f[i]=s+Sum[i-1];
        }
        ll ans=-1;
        for(int i=1;i<=N;i++)
        {
            int l=i,r=N;
            while(l<=r)
            {
              int mid=(l+r)/2;
              if(Sum[mid]<=f[i])
              {
                  l=mid+1;
                  ans=max(Sum[mid]-Sum[i-1],ans);
              }
              else
              {
                  r=mid-1;
              }
            }
        }
        if(ans==-1)
            cout<<"averyboynb"<<endl;
        else
           cout<<ans<<endl;
    }
    return 0;
}

Description

美旸旸现在在腾讯实习。他每工作一天,公司就会给他发一天的工资,但是,当天的工资不一定要当天领,可以等到下次领的时候一起领,但是每次领工资时,之前所有没有领的工资必须这次都领完。现在给你美旸旸工作N天的工资a[1]~a[N],然后给你一个整数M,代表美旸旸一共要领M次工资(不能多也不能少)并且最后一定要把N天的工资领完,毕竟都是美旸旸赚的血汗钱。也就是第N天他是一定要领工资的。(不然就领不完工资了。。。)。现在问题是让你帮美旸旸选择一种领工资的方案,使得这M次领的工资的最大值最小,毕竟美旸旸是一个低调的人,他不想一次领太多钱。你能帮助他吗?

Input

第一行一个整数T(T <= 5)代表测试数据的组数
接下来T组测试数据。
每组测试数据的第一行包括两个整数N, M其含义如上(1 <=N <= 1e5, 1 <= M <= N)
接下来一行N个整数,代表美旸旸每天的工资a[i](1 <= a[i] <= 1e5)

Output

对于每组测试数据输出一个整数,代表所有领工资方案中,领的工资最大值最小的那一种方案的最大值。

Sample Input

1
7 5
100
400
300
100
500
101
400

Sample Output

500

HINT

第一次领第一天和第二天的工资,一共100 + 400 = 500,第二次领第三天和第四天的工资,一共300 + 100 = 400,第三次领第五天的工资,一共500,第四次领第六天的工资,一共101,第五次领第七天的工资,一共400.所以这五次领的工资的 最大值为500.在这种方案下,最大值(500)比其他方案的最大值要小。

#include<iostream>
#include<cstdio>
#define MAX 1000010
using namespace std;
int a[MAX];
int n,m;
bool judge(int x)
{
    int t=0,sum=0;
    for(int i=1;i<=n;i++)
    {
        sum+=a[i];
        if(sum>x)
        {
            sum=a[i];
            t++;
        }
        if(t==m)
            return false;
    }
    return true;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
       int Max=0,total=0,ans=0;
       cin>>n>>m;
       for(int i=1;i<=n;i++)
       {
           cin>>a[i];
           Max=max(Max,a[i]);
           total+=a[i];
       }
       int l=Max,r=total;
       while(l<=r)
       {
           int mid=(l+r)>>1;
           if(judge(mid))
           {
               ans=mid;
               r=mid-1;
           }
           else l=mid+1;
       }
       cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hzaukotete/article/details/81191259