AcWing789题
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int main() {
int arr[N],n,q,k,l,r,mid;
scanf("%d%d",&n,&q);
for (int i = 0; i < n; ++i) scanf("%d",arr+i);
while (q--){
scanf("%d",&k);
l=0,r=n-1;
while (l<r){
//找右半部分,满足性质a[i]>=x的最小位置
mid=l+r>>1;
if(arr[mid]>=k) r=mid;
else l=mid+1;
}
if(arr[l]!=k) printf("-1 -1\n");//如果数组中不存在这样数,到下标n-1就会停止,需要判断
else{
printf("%d ",l);
l=0,r=n-1;
while (l<r){
//找左半部分,满足性质a[i]<=x的最大位置
mid=l+r+1>>1;
if (arr[mid]<=k) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
}
return 0;
}
卖古董 Problem1211
你的朋友小明有n个古董,每个古董的价值给出,然后小明要在接下来的m天内将所有古董依次卖出(注意:必须依次卖出,也就是从第一个开始卖卖到最后一个),小明希望的是这m天内每天卖出的价值和的最大值最小,你来帮助他吗?
输入格式
一共T组数据,每组一个n和m,代表n个古董以及m天(1<=m<=n),然后下面n行为古董的价值,古董价值为1到10000(1<=n<=100000)
输出格式
输出m天内卖出的古董价值和的最大值(当然是最优的时候),我们希望的是这个最大值越小越好
输入样例
3
7 5
100
400
300
100
500
101
400
4 3
2
6
2
4
4 2
2
6
2
4
输出样例
500
6
8
提示
分成几段 {100,400}{300,100},{500},{101}{400};这里分成了5段; 每段都 <=500;
500是很多成功分段方案中,每段的最大值当中最小的一组;
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int q[N],n,m;
bool check(int dollar)
{
bool tag= false;
int temp=dollar,day=1;//day记录分成的天数,默认第一个就是第一天卖的第一件古董。
for (int i = 0; i < n;) {
if(temp<q[i]) {
day++,temp=dollar;}//此时q[i]就是下一天卖的第一件物品
else temp-=q[i++];
}
if(day<=m) tag= true;//我们希望找出右半部分,mid的最小值满足day<=m
return tag;
}
int main() {
int T,max,sum;
cin>>T;
while(T--){
scanf("%d%d",&n,&m);
max=-1,sum=0;
for (int i = 0; i < n; ++i){
scanf("%d",q+i);
if(max<q[i]) max=q[i];
sum+=q[i];
}
int l=max,r=sum;
while (l<r){
int mid=l+r>>1;
if (check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
return 0;
}
小清新切绳子-二分 Problem1648
#include <bits/stdc++.h>
using namespace std;
typedef long l;
int Li[10005],K,n;
bool check(int len)
{
bool tag=false;
l k=0; //怕k超出范围,用long类型
for (int i = 0; i < n; ++i) k+=Li[i]/len;
if(k>=K) tag=true; //为1说明满足要求 为0不满足要求
return tag; //我们要求满足要求下的最大值len
}//为了快点或者节省空间,用bool类型
int main()
{
l max,sum;
while (~scanf("%d%d",&n,&K))
{
max=-1,sum=0;
for (int i = 0; i < n; ++i) {
scanf("%d",Li+i);
if (max<Li[i]) max=Li[i];
sum+=Li[i];
}
int l=max/K,r=sum/K;//避免太盲目 自己先给定最可能的区间
while (l<r){
int mid=l+r+1>>1;
if (check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",r);
}
return 0;
}
模板
整数二分
一、区间[l,r]被划分成[l,mid] [mid+1,r]时使用
适用情况:右半部分,求>=x的最小数时使用
int search_1(int l,int r)
{
while(l<r){
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
return l; //return r;都行
}
二、区间[l,r]被划分成[l,mid-1] [mid,r]时使用
适用情况:左半部分,求<=x的最大数时使用
int search_2(int l,int r)
{
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
}
实数二分
double search(double l,double r)
{
while(r-l>0.0000001)//一个误差,或者直接写成for(int i=0;i<n;++i)
{
//不管三七二十一,循环n次结束
double mid=(r+l)/2;
if(check(mid)) r=mid;//l=mid;
else l=mid;//r=mid;
}
return l;
}