二分算法有感

刷完郭老师的课程里的二分后有些心得发表发表㉿。 

小技巧类:

1二分可以设置last,来保证答案稳定正确

2可以专门用宏写一个debug代码,方便debug

方法类:

1二分题可以分为整数二分小数二分

整数二分的lr需考虑-1,+1

小数二分则不需要

2cheek()函数可以用模拟的思想,将带入值一个一个判断全部数组

3l,r代表两极端值

思想类:

二分的东西可能很抽象,可以通过思考两边极端再进一步推位于极端的最优解

007:Aggressive cows

Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,...,xN (0 <= xi <= 1,000,000,000).

His C (2 <= C <= N) cows don't like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?

输入

* Line 1: Two space-separated integers: N and C

* Lines 2..N+1: Line i+1 contains an integer stall location, xi

输出* Line 1: One integer: the largest minimum distancew​​​​​​​f

AC代码

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int n=1e6+10;
int C,N,stall[n];
bool cheek(int mid){
    int now =0,last =stall[0],numc=1;
    for(int i=1;i<N;i++){
            now=stall[i];
            if(now-last>=mid){
                numc++;
                if(numc==C)return 1;//一个一个放
                last=now;
            }
    }
    return 0;
}
int main(){
    cin>>N>>C;
    for(int i=0;i<N;i++){
        cin>>stall[i];
    }
    sort(stall,stall+N);
    int l=stall[0],r=stall[N-1],mid=0,lastmid;
    while(l<=r)//l<=r都怎么去写
    {
        mid=l+(r-l)/2;//二分
        if(cheek(mid))
        {
            l=mid+1;
            lastmid=mid;
        }else r=mid-1;
    }
    if(!cheek(mid))mid=lastmid;//一般这种cheek函数的可以用last来确保答案
    cout<<mid;
    return 0;
}

007​​和老师讲的差不多,但是​​​​​r或l的值最后总会有些许误差,所以应该用lastmid来当作答案

008:派

描述

我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。

我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。

请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。

输入

第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。

输出

输出每个人能得到的最大的派的体积,精确到小数点后三位。

008的判断方法需要注意,如果取l,r为每个人分到的派的体积,那么最后答案偏大。

应该二分的是每个人分到的派的半径

AC代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
const int n=1e6+10;
#define PI 3.141592653589793
int N,F;
double R[n];
bool check(double tv){
    int num=0;
    for(int i=0;i<N;i++){
        num+=(int) (R[i]*R[i])/(tv*tv);
        if(num>=F+1)return 1;
    }
    return 0;
}
int main(){
    cin>>N>>F;
    for(int i=0;i<N;i++)cin>>R[i];
    sort(R,R+N);
    double l=0,r=R[N-1],mid=0;
    while(fabs(r-l)>1e-9){
        mid=l+(r-l)/2;
        if(check(mid)){
            l=mid;
        }else r=mid;
    }
    mid=mid*mid*PI;
    cout<<fixed<<setprecision(3)<<mid;
    return 0;
}
//小数二分,注意精度问题,r=mid,l=mid !!!

009:月度开销

描述

农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。

约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。

约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。

输入

第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。

输出

一个整数,即最大月度开销的最小值。

月度开销的cheek方法我一开始想多了,认为应该讲每个财政周期尽可能的填满,也就是判断某个财政周期最大值的最优解是否符合。但是想了想觉得,如果已经是最优解了,内部costday是可以随意调换的,也就只需要判断非最优解就行了

AC代码

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int n=1e6;
int N,M;
int cost[n];
bool check(int n)
{
    int m=1;
    int curCost=0;
    for(int i=0;i<N;i++){
        if(cost[i]>n)return false;
        if(curCost+cost[i]>n){
            m++;
            curCost=cost[i];
            if(m>M)return false;
        }
        else{
            curCost+=cost[i];
        }
    }
    if(m>M)return false;
    else
    return true;
}
int main(){
   cin>>N>>M;
   int l=1<<30,r=0,last;
   for(int i=0;i<N;i++){
       cin>>cost[i];
       int t=cost[i];
       r+=t;
       l=min(l,t);
   }
   while(l<=r){
        int mid=l+(r-l)/2;
        if(check(mid)){
            r=mid-1;
            last=mid;
        }else l=mid+1;
   }
//一般这种带复杂判断的都要设置一个last
    return cout<<last,0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值