目录
一、算法概要
首先要知道二分是干什么的和要求
(1)二分的返回值返回的是第一个大于等于指定数的数值
(2)要二分要保证序列有序 而有序不一定能二分
这个是从左向右找到x
int l=0,r=n-1; //表示左边界和右边界
while(l<r) //左边界要小于右边界才移动
{
int mid=l+r>>1; //取中间的数 满足右边数字一定小于左边 左边的数字一定大于右边
if(q[mid]>=x) r=mid; //mid表示下标 q[mid]找到的就是中间的数 因为有序
else l=mid+1;
}
至于为什么mid=l+r>>1 r=mid l=mid+1 是因为边界问题
不这样设置的话会出现死循环
这个边界证明基本遇不到 记住这样写就可以了 不过多解释
感兴趣的话可以去acwnig 题库789题题解里有解释
大概意思就是带个0 1 进去 返回后形成的边界依旧是0 1
这个是从右向左找到x 之所以这样设置依旧是边界问题
l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
整数二分就要注意边界 小数二分就不用
double l=-10000,r=10000;
while(r-l>1e-8)
{
double mid=(l+r)/2;
if(q[mid]>=x) r=mid;
else l=mid;
}
所以这里用到的就是
二分的返回值返回的是第一个大于等于指定数的数值
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
int q[N];
int n;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
while(m--)
{
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[l]!=x) printf("-1 -1\n");
else
{
printf("%d ",l);
l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
}
return 0;
}
二、推广
那么我们现在对二分的概念进行推广
首先二分是有序的 也即是我在这个序列找一个元素x
在x之前的元素我认为都是满足条件的元素, 而在x后面的数都是不满足条件的元素
从这个概念出发我们解下面这个题目
这其实是一道指数题目但是我们也可以从二分的角度去理解它
输入的第一行包含 N 和 L。
(一)二分解
我们现在选取h篇文章,要求找到 满足条件
找到引用指数大于等于h的文章 这些文章总数大于等于h 满足条件h的最大值
每篇文章至多再被引用一次 总共可以引用的次数是L
那很明显这些引用次数比h小1的文章 在被引用一次就可以满足条件了
所以要记录一下这些文章总数bns 总次数最多L 然后比对和L的大小选小的
再记录一下满足大于h的文章总数ans
所以最后能满足大于等于h的文章总数就是ans+min(L,bns)
我们现在来想h指数为A 和 A-1 的文章
引用指数大于等于A的文章一定大于A-1 所以A-1一定是成立的
我们现在假设
最大的h指数为A 那么h指数为A-1 一定是成立的
指数为A+1一定是不成立的 h指数从小到大
从左向右遍历 ,二分找到的就是最大的那个h指数
用check找到最大的h指数
int l=0,r=N;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
bool check(int x)
{
int ans=0,bns=0; //之前有解析
for(int i=1;i<=n;i++)
{
if(x<=c[i]) ans++; //c[i]表示的就是引用指数 ans是大于等于篇数x的文章总和
else if(x-c[i]==1) bns++; //引用指数比篇数x小1的文章数量总和
}
bns=min(bns,k); //找到最大可以新增多少满足条件的文章
ans+=bns;
if(ans>=x) return true;
else return false;
}
最后答案就是
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
int n,k;
int c[N];
bool check(int x)
{
int ans=0,bns=0;
for(int i=1;i<=n;i++)
{
if(x<=c[i]) ans++;
else if(x-c[i]==1) bns++;
}
bns=min(bns,k);
ans+=bns;
if(ans>=x) return true;
else return false;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
int l=0,r=N;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d",l);
return 0;
}
(二)h指数一般做法
先对其进行排序
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, L;
int q[N];
int main()
{
scanf("%d%d", &n, &L);
for (int i = 1; i <= n; i ++ ) scanf("%d", &q[i]);
sort(q + 1, q + n + 1, greater<int>()); //从大到小排序
int res = 0; //从小到大枚举h指数和文章引用次数
for (int i = 1, j = n; i <= n; i ++ ) //i=1时h指数最小 j=n文章引用次数最小
{
while (j && q[j] < i) j -- ;
if (q[i] >= i - 1 && i - j <= L) //描述的那两个条件
res = i; //满足就更新最大值
}
printf("%d\n", res);
return 0;
}
题目来源acwing
模板来源:闫学灿
链接:https://www.acwing.com/activity/content/code/content/57785/
来源:AcWing