递增排序,递减排序,非递减排序,非递增排序
1,2,3,4,5,.:递增排列
9,8,7,6,5.:递减排列
1,2,3,3,4,5,8,8,.:非递减排列
9,8,7,7,6,5,5,2,1,.:非递增排列
二分查找&&二分答案(部分内容参照二分查找和[ShawnZhou (https://www.luogu.org/blog/user20197/solution-p2678))
使用二分需要满足两个条件。一个是有界,一个是单调。如果题目规定了有“最大值最小”或者“最小值最大”的东西,那么这个东西应该就满足二分答案的有界性(显然)和单调性(能看出来)。
二分查找依赖于一个有序的集合。开始时,先找出有序集合中间的那个元素。如果此元素比要查找的元素大,就接着在较小的一个半区进行查找;反之,如果此元素比要找的元素小,就在较大的一个半区进行查找。在每个更小的数据集中重复这个查找过程,直到找到要查找的元素或者数据集不能再分割。
查找数列中第一个大于某值的下标。也就是c++里的lower_bound()
手写出来是这样的:
int find(int a[],key,len)
{
int left=0;
int right=len;
int mid;
while(left<=right)
{
mid=(left+right)>>1;
if(a[mid]<key)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
return left;
}
二分答案的题:
- P2678 跳石头
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int d,n,m;
int a[MAXN];
int l,ans,r,mid;
bool check(int x)
{
int cnt=0;
int i=0;
int now=0;
while(i<n+1)
{
i++;
if(a[i]-a[now]<x)
{
cnt++;
}
else
{
now=i;
}
}
if(cnt>m) return false;
else return true;
}
int main()
{
scanf("%d%d%d",&d,&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
a[n+1]=d;
l=1;r=d;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<ans;
return 0;
}
P1316 丢瓶盖和P1824 进击的奶牛
这道题和跳石头很像,不同的地方是题中所给的为留下多少而不是拿走多少,还有就是需要对坐标进行排序
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 1001000
using namespace std;
int n,k,b;
int l,r,mid,ans;
int a[MAXN];
bool check(int x)
{
int i=1;
int now=1;
int cnt=0;
while(i<n)
{
i++;
if(a[i]-a[now]<x)
{
cnt++;
}
else
{
now=i;
}
}
if(cnt>k) return false;
else return true;
}
int main()
{
scanf("%d%d",&n,&b);
k=n-b;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
l=1;
r=a[n];
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<ans;
return 0;
}
P1873 砍树
这道题除了要记得开long long 好像也没什么特别需要注意得了
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define MAXN 20000010
using namespace std;
ll n,m;
ll l,r,ans,mid;
ll a[MAXN];
bool check(ll x)
{
ll cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]>x)
{
cnt=cnt+(a[i]-x);
}
}
if(cnt>=m) return true;
else return false;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
sort(a+1,a+n+1);
l=1;
r=a[n];
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<ans;
}
P1577 切绳子
这道题因为小数的精度不够,所以小数的二分是另一种写法
while(l+eps<=r)
{
mid=(l+r)/2.00000000;
if(check(mid))
{
ans=mid;
l=mid;
}
else r=mid;
}
int aa=(int)((ans+eps)*100);
printf("%.2lf",1.0*aa/100.0);
这道题除了用小数写也可以用整数写最后转化为小数
这道题的AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
#define eps 1e-8
using namespace std;
int n,k;
double a[MAXN];
double l,r,mid,ans;
bool check(double x)
{
int cnt=0;
for(int i=1;i<=n;i++)
{
int p=floor(a[i]/x) ;
cnt=cnt+p;
}
if(cnt>=k) return true;
else return false;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lf",&a[i]);
}
sort(a+1,a+n+1);
l=0.00000000001;
r=a[n];
while(l+eps<=r)
{
mid=(l+r)/2.00000000;
if(check(mid))
{
ans=mid;
l=mid;
}
else r=mid;
}
int aa=(int)((ans+eps)*100);
printf("%.2lf",1.0*aa/100.0);
return 0;
}
P1843 奶牛晒衣服
这道题如果每一步进行贪心的话会TLE,所以可以进行一些简单的优化
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 2000001
#define ll long long
using namespace std;
ll n,a;
double b;
ll l,r,mid,ans;
ll tot;
ll A[MAXN],B[MAXN];
bool check(ll x)
{
for(int i=1;i<=n;i++)
{
A[i]=B[i];
}
for(int i=1;i<=n;i++)
{
A[i]=A[i]-a*x;
}
for(int i=1;i<=n;i++)
{
tot=tot+A[i];
}
if((tot/b)>x) return false;
int cur=0;
for(int i=1;i<=n;i++)
{
if(A[i]>0&&A[i]<=b) cur++;
else if(A[i]>b)
{
cur=cur+ceil(A[i]/b);
}
}
if(cur>x) return false;
else return true;
}
int main()
{
scanf("%lld%lld%lf",&n,&a,&b);
for(int i=1;i<=n;i++)
{
scanf("%lld",&A[i]);
}
for(int i=1;i<=n;i++)
{
B[i]=A[i];
}
sort(A+1,A+n+1);
tot=0;
l=1;
r=A[n]/a+1;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
cout<<ans;
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define MAXN 1000001
#define M 100000000
using namespace std;
int a[MAXN];
map<int,int> b;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=1000000;i++)
{
if(a[i]==0) continue;
// cout<<i<<endl;
b[a[i]]=i;
}
sort(a+1,a+n+1);
int q;
scanf("%d",&q);
while(q--)
{
int m;
bool flag=0;
scanf("%d",&m);
int l=1,r=a[n];
int mid;
while(l<=r)
{
// printf("l=%d,r=%d
",l,r);
mid=(l+r)>>1;
// printf("mid=%d
",mid);
if(mid==m)
{
printf("%d
",b[m]);
flag=1;
// cout<<"aaa"<<endl;
break;
}
if(mid>m)
{
r=mid-1;
// cout<<"bbb"<<endl;
}
if(mid<m)
{
l=mid+1;;
// cout<<"ccc"<<endl;
}
}
if(!flag)
{
printf("0
");
// cout<<"ddd"<<endl;
}
}
return 0;
}