一、前言:
lower_bound(val): 返回容器中第一个值【大于或等于】val的元素的iterator位置。
upper_bound(val): 返回容器中第一个值【大于】val的元素的iterator位置。
二、题目:
1、1085 Perfect Sequence
分析:题目要求选取满足要求的最长序列。能使选取方案数目最大的一定是该递增序列连续的若干个数。可以用lowwer_bound函数,查找第一个满足条件的位置。
注意点:这里有乘法,需要开longlong,要不最后一个测试点过不了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 200100
using namespace std;
typedef long long ll;
ll a[maxn];
ll n,p,ans=1;
int main(){
std::ios::sync_with_stdio(false);
cin>>n>>p;
for(ll i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
for(int i=0;i<n;i++){
ll tmp=a[i]*p;
ll cur=upper_bound(a,a+n,tmp)-a;
ans=max(ans,cur-i);
}
cout<<ans<<endl;
return 0;
}
2、1010 Radix
分析:先前写过,二分第二个数的进制即可.
https://wangfx.blog.csdn.net/article/details/88652088
3、1044 Shopping in Mars
题意:要求打印出所有和为S的子序列的起点和终点,如果没有和为s的子序列,那么打印出大于和S(大于S最小)的那个子序列的起点和终点。
分析:
先前求出前缀和sum[i];
然后扫描前缀和sum[i]两遍。第一遍扫描,枚举每一个起点,二分查找大于和为m的终点的位置,从而找到距离m最近的值。第二遍扫描,找到所有距离为刚刚求出来的最近的值。
由于前缀和是严格单调递增的,所以这里lower_bound()和upper_bound是等价的,如果是不减,那么这里
开始的时候,自己的二分是这样写的,一直WA。
这种二分的写法应该是适用于查找一个单调序列中是否存在一个数x,存在就返回坐标。
(1)查找单调序列中是否存在一个元素x的写法
int solve(int l,int r,int i){
int base=l,top=r,mid;
int sumSeq;
while(base<=top){
mid=(base+top)/2;
sumSeq=sum[mid]-sum[i-1];
if(sumSeq>m){
top=mid-1;
}
else if(sumSeq<m){
base=mid+1;
}
else{
break;
}
}
return mid;
}
(2)查找单调序列中,第一个大于x、最后一个小于x的元素的位置(实际上就是实现了lower_bound和upper_bound)的写法。
https://www.cnblogs.com/dilthey/p/9903668.html
得分:25/25
直接用lower_bound()做。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 100010
using namespace std;
int n,m;
int a[maxn],sum[maxn];
vector<int> v1,v2;
int main(){
std::ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
//查找距离m最小的值
int minn=inf;
for(int i=1;i<=n;i++){
int rr=sum[i-1]+m;
int pos=lower_bound(sum,sum+n,rr)-sum;
int sumSeq=sum[pos]-sum[i-1];
//cout<<"sumSeq "<<sumSeq<<endl;
if(sumSeq==m){
minn=m;
break;
}
else if(sumSeq<minn &&sumSeq>m){
minn=sumSeq;
}
//cout<<pos<<endl;
}
//把所有长度为minn的都存储
for(int i=1;i<=n;i++){
int rr=sum[i-1]+m;
int pos=lower_bound(sum,sum+n,rr)-sum;
int sumSeq=sum[pos]-sum[i-1];
if(sumSeq==minn){
v1.push_back(i);
v2.push_back(pos);
}
}
for(int i=0;i<v1.size();i++){
cout<<v1[i]<<"-"<<v2[i]<<endl;
}
return 0;
}
手写lower_bound。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 100010
using namespace std;
typedef long long ll;
vector<int> v1,v2;
int a[maxn],sum[maxn];
int m,n,pos;
int main(){
std::ios::sync_with_stdio(false);
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
sum[i+1]=sum[i]+a[i+1];
}
int minn=inf;
//第一遍扫,求出距离m的最近值是多少
for(int i=1;i<=n;i++){
int base=i,top=n,mid,sumSeq;
while(base<top){
mid=(base+top)/2;
sumSeq=sum[mid]-sum[i-1];
if(sumSeq>=m){
top=mid;
}
else{
base=mid+1;
}
}
sumSeq=sum[top]-sum[i-1];
if(sumSeq==m){
minn=sumSeq;
break;
}
else if(sumSeq<minn&&sumSeq>m){
minn=sumSeq;
}
}
for(int i=1;i<=n;i++){
int base=i,top=n,mid,sumSeq;
while(base<top){
mid=(base+top)/2;
sumSeq=sum[mid]-sum[i-1];
if(sumSeq>=m){
top=mid;
}
else{
base=mid+1;
}
}
sumSeq=sum[top]-sum[i-1];
//cout<<i<<" "<<base<<" "<<sumSeq<<endl;
if(sumSeq==minn){
v1.push_back(i);
v2.push_back(base);
}
}
for(int i=0;i<v1.size();i++){
cout<<v1[i]<<"-"<<v2[i]<<endl;
}
return 0;
}
4、1048 Find Coins (25 分)
题意分析:查找是否存在一对匹配的a[i]和a[j],使两者的和为m。
排序,扫描,二分查找是否存在m-a[i]即可。
这里要注意的是,判断的时候要确保
i
!
=
j
i ! =j
i!=j。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 100010
using namespace std;
int n,m;
int a[maxn];
int solve(int x){
int l=0,r=n-1,mid;
while(l<=r){
mid=(l+r)/2;
if(a[mid]>x){
r=mid-1;
}
else if(a[mid]<x){
l=mid+1;
}
else{
return mid;
}
}
return -1;
}
int main(){
std::ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
bool flag=false;
for(int i=0;i<n;i++){
int num=m-a[i];
//二分查找num是否在数列中存在
int pos=solve(num);
if(pos!=-1 &&i!=pos){
flag=true;
cout<<a[i]<<" "<<a[pos]<<endl;
break;
}
}
if(!flag) cout<<"No Solution"<<endl;
return 0;
}