二分答案
- 貌似题目让你求啥就要对什么东西进行二分
实数二分
-
注意精度,如果要求输出两位小数,建议二分到三、四位
-
还是注意精度,P1542不用 long double 都过不去
P1024 [NOIP2001 提高组] 一元三次方程求解
-
对根所在区间进行二分
-
因为 ’根与根的差的绝对值 \(\ge 1\) ‘,所以枚举 $-100 \to 100 $ 中的每个整数即可,二分的区间的 \(l,r\) 分别是 \(i,i+1\)
-
进行二分的条件:\(f(i) \times f(i+1) \le 0\)
-
如果 \(f(i) \times f(i+1) = 0\),且 \(f(i)=0\),那么方程的其中一个根就是 \(i\)
-
如果 \(f(i+1)=0\),暂时不管他,在枚举到 \(f(i+1),f(i+2)\) 时再输出,防止重复
-
-
关于 \(check()\):
-
由题目得,如果 $f(x) \times f(y) <0 $,则在 \([x,y]\) 内存在一个根
-
所以如果 \(f(l) \times f(mid) <0\),就让 \(r=mid\),因为此时根在 \([l,mid]\) 中,反之 \(l=mid\)
-
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const double ACC=1e-7;
int cnt;
double a,b,c,d;
double check(double x){
return 1.0*a*x*x*x+1.0*b*x*x+1.0*c*x+d;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>a>>b>>c>>d;
for(int i=-100;i<100;i++){
double l=i,r=i+1;
double x=check(l),y=check(r);
if(!x){
printf("%.2lf ",double(l));
cnt++;
continue;
}else{
if(x*y>=0) continue;
while(r-l>ACC){
double mid=(l+r)/2;
if(check(mid)*check(l)<=0) r=mid;
else l=mid;
}
printf("%.2lf ",l);
cnt++;
}
if(cnt==3) break;
}
}
P1542 包裹快递
-
对速度进行二分
-
‘最大的最小’ 意味着要在当前值符合条件的情况下把右指针左移(在速度更小的区间里寻找符合条件的值)
-
题目卡精度卡的很严,必须要用 long double
-
关于 \(check()\) :
-
题目要求最大速度的最小值,由样例解释可得,每段路程的速度是可变的,只要全程的最大速度最小即可
-
二分到的一个速度不符合条件,意味着至少有一段路程,用最大速度(二分到的速度)跑,仍然赶不上截止期限 \(y[i]\)
-
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const int N=2e5+10;
int n;
int x[N],y[N],s[N],sum;
long double l,r;
bool check(long double max_sp){
long double ti=0;
for(int i=1;i<=n;i++){
if(ti+1.0*s[i]/max_sp>y[i]) return 0;
else ti+=1.0*s[i]/max_sp;
if(ti<x[i]) ti=x[i];
}
return 1;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i]>>s[i];
sum+=s[i];
}
l=0; r=1e9;
while(r-l>0.0001){
long double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.2Lf\n",l);
}
P1577 切绳子
-
对切割成的绳子的长度进行二分
-
输入的绳长都是两位小数,干脆把他们都 \(\times 100\),把这道题转化成整数二分来做
-
关于 \(check()\):
-
对于二分出来的一个长度,枚举每一根绳子,把每根绳子能割出来的绳子数相加
-
如果加和 \(sum < k\),说明当前长度太长,令 \(r=mid\)
-
反之令 \(l=mid+1\)
-
代码
#include <bits/stdc++.h>
using namespace std;
int n,k;
double in;
int len[10010];
int judge(int length){
int cnt=0;
for(int i=1;i<=n;i++) cnt+=len[i]/length;
return cnt>=k;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>in;
len[i]=in*100;
}
int l=0;
int r=1e9;
int res=0;
while(l<r){
int mid=l+(r-l)/2;
if(mid==0) break;
if(judge(mid)){
l=mid+1;
res=mid;
}
else r=mid;
}
printf("%.2lf",res*1.0/100);
}
整数二分
P1824 进击的奶牛
-
对两头牛间的最近距离进行二分
-
关于 \(check()\):
-
贪心,用 \(p\) 记录上一头牛的位置,一旦这个牛棚距离 \(p\) 的距离 \(\ge\) 二分到的最小距离,就 \(++cnt\) ,并且把 \(p\) 移动到该牛棚
-
采用贪心能保证牛棚在该 ‘最小距离’ 下尽可能多的放牛
-
代码
#include <bits/stdc++.h>
using namespace std;
int pos[100010];
int n,c;
bool judge(int len){
int cnt=1;
int p=1;
for(int i=2;i<=n;i++){
if(pos[i]-pos[p]>=len){
cnt++;
p=i;
}
}
return cnt>=c;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>pos[i];
sort(pos+1,pos+1+n);
int l=0;
int r=pos[n];
int res=r;
while(l<r){
int mid=l+(r-l)/2;
if(judge(mid)){
res=mid;
l=mid+1;
}
else r=mid;
}
cout<<res;
}
P1182 数列分段 Section II
-
对每段的和的最大值进行二分
-
因为求的是 ’最大值最小‘ ,所以当前值符合条件的情况下:
-
如果根据当前最大值分的快多于 \(m\) ,就将 \(l\) 右移,增大最大值,使数列能被分为更少的块
-
反之将 \(r\) 左移,使分的块更多或在块数符合条件的情况下使最大值最小
-
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
int n,m;
int a[100001];
int l,r;
bool check(int maxi){
int sum=0,cnt=0;
for(int i=1;i<=n;i++){
if(a[i]>maxi) return 0;
if(sum+a[i]>maxi){
sum=a[i];
cnt++;
}else
sum+=a[i];
}
return cnt>=m;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
r+=a[i];
l=max(l,a[i]);
}
while(l<r){
int mid=(l+r)/2;
if(check(mid)) l=mid+1;
else r=mid;
// cout<<l<<" "<<r<<"\n";
}
cout<<l<<"\n";
}
P2440 木材加工
-
对分成的木头的长度进行二分
-
因为有可能根本分不出 \(k\) 段来,所以 \(l\) 初始为 \(0\),相应的,
while(l < r)改为while(l+1 < r) -
\(check()\) 部分与 P1577 切绳子 差不多,翻到上面看就行
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
int n,k;
int len[100001];
int check(int x){
int cnt=0;
for(int i=1;i<=n;i++)
cnt+=(len[i]/x);
return cnt;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>len[i];
int l=0,r=1e18;
while(l+1<r){
int mid=(l+r)/2;
if(check(mid)>=k) l=mid;
else r=mid;
}
cout<<l<<"\n";
}
P1873 [COCI 2011/2012 #5] EKO / 砍树
- 对锯子的最大高度进行二分
代码
#include <bits/stdc++.h>
using namespace std;
long long hei[1000010];
long long n,m;
bool judge(long long len){
long long cnt=0;
long long p=0;
for(long long i=1;i<=n;i++){
if(hei[i]>len) cnt+=hei[i]-len;
}
return cnt>=m;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(long long i=1;i<=n;i++) cin>>hei[i];
sort(hei+1,hei+1+n);
long long l=0;
long long r=hei[n];
while(l+1<r){
long long mid=l+(r-l)/2;
if(judge(mid)) l=mid;
else r=mid;
}
cout<<res;
}
P1083 [NOIP2012 提高组] 借教室
-
对有多少订单符合要求进行二分
-
关于 \(check()\):判断是否有订单不满足要求时用差分进行处理
bool check(int x){ memset(temp,0,sizeof(temp)); for(int i=1;i<=x;i++){ temp[s[i]]+=d[i]; temp[t[i]+1]-=d[i]; } for(int i=1;i<=n;i++){ temp[i]+=temp[i-1]; if(temp[i]>r[i]) return 0; } return 1; }
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const int N=1e6+10;
int n,m;
int r[N];
int d[N],s[N],t[N];
int temp[N];
bool check(int x){
memset(temp,0,sizeof(temp));
for(int i=1;i<=x;i++){
temp[s[i]]+=d[i];
temp[t[i]+1]-=d[i];
}
for(int i=1;i<=n;i++){
temp[i]+=temp[i-1];
if(temp[i]>r[i])
return 0;
}
return 1;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>r[i];
for(int i=1;i<=m;i++)
cin>>d[i]>>s[i]>>t[i];
if(check(m)){
cout<<"0\n";
return 0;
}
int l=1,r=m;
while(l<r){
int mid=(l+r)/2;
if(!check(mid)) r=mid;
else l=mid+1;
}
cout<<"-1\n"<<r<<"\n";
}
浙公网安备 33010602011771号