二分模板 Acwing 789 数的范围

 

二分一定有解,若出现无解,一定是题目中无解二分步骤:定义check函数,先找到一个x,使得区间左边满足条件区间右边不满足条件,

定义mid = l + r >> 1去判断于x的关系,此时需要判断边界关系,例如当a[mid]小于x时,说明二分值在x的左边,此时缩小范围为【mid,r】,

即令 l = mid,此时返回check函数,使 mid = l + r +1 >> 1,因为在这里mid是向下取整,不加一的话,当 l  = r - 1是 ,mid = l,此时循环后的区间

还是【l,r】,会形成死循环,所以 + 1后范围会变成 mid = r ,得到我们的x的第一次出现的值,在循环最后一次,我们的 l 和 r都在x 的两侧

此时 l = r = x,因此当无解时,判断最后一轮a[ l ] != x ,即为无解,此题要判断两次,所以再进行一次二分,判断x的结束的位置,以下为代码

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 100010;
 5 int n,m;
 6 int a[N];
 7 
 8 int main()
 9 {
10     scanf("%d%d", &n, &m);
11     for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
12     
13     while (m -- )
14     {
15         int x,l = 0, r = n - 1;
16         scanf("%d", &x);
17         
18         while(l < r)
19         {
20             int mid = l + r >> 1;
21             if(a[mid] >= x) r = mid;
22             else l = mid + 1;
23         }
24         if(a[l] != x) cout <<"-1 -1" << endl;
25         
26         else
27         {
28             cout << l << ' ';
29             
30             int l = 0,r = n - 1;
31             
32             while(l < r)
33             {
34                 int mid = l + r + 1>> 1;
35                 if(a[mid] <= x)  l = mid ;
36                 else r = mid - 1;
37             }
38             cout << l << endl;
39         }
40     }
41     return 0;
42 }
Code
复制代码

例题:Problem - B - Codeforces  通过二分不断缩小区间取搜索满足条件的最小的天数

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 
 5 const int N = 210,inf = 1e9;
 6 int n,p,w,t; 
 7 int _;
 8 
 9 signed main()
10 {
11     ios::sync_with_stdio(0);
12     cin.tie(0);
13     cout.tie(0);
14     cin >>_;
15     while(_ --)
16     {
17         cin >> n >> p >> w >> t;
18         int l = 0,r = n;
19         int ans = n;
20         //cout << n << p << w << t;
21         while(l <= r)
22         {
23             int mid = (l + r) / 2;
24             //cout << mid << " ";
25             int renwu = (n - 1) / 7 + 1;
26             int zong = 0;
27             if(mid * 2 >= renwu){ //当前天数*2大于任务总数
28                 zong += renwu * t; //只用任务数×每个任务的分数
29             }else 
30             {//否则只用当前区间内的数字*2*每个任务的分数
31                 zong += mid * 2 * t;
32             }
33             zong += mid * w; //再加上这个二分区间每天上课的分数
34             if(zong >= p)//如果分数多了,区间向左边移动
35             {
36                 ans = min(ans,mid);
37                 r = mid - 1;
38             }//否则分数若不够,区间向右移
39             else {l = mid + 1;}
40         }
41         //cout << n <<" " << ans << " ";
42         cout << n - ans << endl;
43     }
44     return 0;
45 }
Code
复制代码

 相关题目: Problem - D - Codeforces

k越大答案一定满足,要求k的最小值,具有单调性可以二分答案。我们每次可以维护x,y区间,那么它的可移动区间就是[x - k,y + k],最后和题目给出的每个

区间取并集,目的是使范围越小越好(精确),新的范围存为tx和ty,如果存在,更新范围,如果不存在,二分更新答案

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4  
 5 const int N = 3e5 + 10,inf = 1e9;
 6 int n,m; 
 7 int _;
 8 int a[N],b[N];
 9  
10 int main()
11 {
12     scanf("%d", &_);
13     while(_ --)
14     {
15       int n;
16       scanf("%d", &n);
17       for (int i = 1; i <= n; i ++ ) scanf("%d%d", &a[i], &b[i]);
18       
19      int l = 0,r = 1e9;
20     
21      while(l < r)
22      {
23          int mid = l + r >> 1;
24          int ok = 1;
25          int x = 0,y = 0;
26          for (int i = 1; i <= n; i ++ )
27          {
28              int tx = max(x - mid,a[i]), ty = min(y + mid,b[i]);
29              if(tx <= ty) x = tx,y = ty;
30              else {
31                  ok = 0;
32                  break;
33              }
34          }
35          if(ok) r = mid;
36          else l = mid + 1;
37      }
38       printf("%d\n", l);
39     }
40     return 0;
41 }
Code
复制代码

 3.Problem - 1692E - Codeforces 这道题要求使数组和为s的最小操作次数,我们可以转换为求一段和为s的最长子序列,长度最长即为操作次数最短

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define endl '\n'
 4 #define int long long
 5 #define cy cout << "YES" << endl
 6 #define cn cout << "NO" << endl
 7 #define c1 cout << -1 << endl
 8 
 9 int _,n,k;
10 const int N = 2e5 + 10,inf = 1e9;
11 const int mod = 1e9 + 7;
12 int s[N],a[N];
13 vector<int> g[N];
14 
15 signed main()
16 {
17     cin >> _;
18     while(_ -- ){
19         cin >> n >> k;
20         int l,r;
21         int p = -inf;
22         for (int i = 1; i <= n; i ++ ) cin >> a[i],s[i] = s[i - 1] + a[i];
23         if(s[n] < k) {cout << -1 << endl;continue;}
24         
25         for (int i = 1; i <= n; i ++ ){
26             l = i,r = n;
27             int ans = 0;
28             while(l < r){
29                 int mid = l + r + 1 >> 1;
30                 if(s[mid] - s[i - 1] <= k) l = mid; //选取一段和为s的最长的子序列可以使得操作次数最少
31                 else r = mid - 1;
32             }
33             if(!l) continue;
34             ans = l - i + 1;
35             p = max(p,ans); //不断更新和为s的序列最长长度
36         }
37         if(p == -inf) c1;
38         else cout << n - p << endl;
39     }
40     return 0;
41 }
Code
复制代码

 

posted @   rw156  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示