二分,三分,01专题

二分最重要的就是check函数的编写以及边界的控制

1.一定区间的完全平方数个数(除二分以外的简单写法)

查看代码

cout << (int)(floor(sqrt(b)) - ceil(sqrt(a)) + 1) << endl;

2.跳石头(为了最大化最小间隙,通过二分跳跃距离,期间通过和撤走石头数量进行比较,来判断此距离是否过短或过长)

查看代码
 bool check(int x)
{
    int sum=0,shi=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]-shi<x)sum++;
        else shi=a[i];
    }
    if(sum>m)return 0;
    return 1;
}

3.Greedy Gift Takers(此题有一个先决条件,如果第x只牛吃不到,那么后面的n-x只都吃不到)

查看代码
 #include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,ma;
int l=0,r=0;
int a[1000000],b[1000000];
int check(int x)//判断x是否能吃到滴牛牛
{
    for(int i=1;i<=x-1;i++)b[i]=a[i];
    sort(b+1,b+x);//排序便于一个接一个的顺序插入
    int bu=n-x;
    for(int i=1;i<=x-1;i++)
    {
        if(b[i]<=bu)bu++;//如果x前面的牛牛都会插入到x后面,就继续
        else return 0;//如果有一个不行就完damn
    }
    return 1;
}
signed main()
{
     cin>>n;
     for(int i=1;i<=n;i++)
     {
         cin>>a[i];
     }
     int p=0;
     r=n+1;
     while(l+1<r)
     {
         int mid=l+r>>1;
         if(check(mid))l=mid;
         else r=mid;
     }
     cout<<n-l<<endl;
     return 0;
}

4.借教室(循环记录借出还入情况绝对会爆,所以采取前缀和的方式来记录)

查看代码
 #include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
int n,m,k,ans,l,r;
int a[1000000],b[1000000],s[1000000],t[1000000],c[1000000],f[1000000];
vector<PII>v;
bool check(int x)
{
    memset(f,0,sizeof f);
    for(int i=1;i<=x;i++)//把前面的需求和返还情况都总结出来
    {
        f[s[i]]+=b[i];
        f[t[i]+1]-=b[i];
    }
    //原理就是用前缀和来看当天的教室借出返还情况,再进行比较
    for(int i=1;i<=n;i++)
    {
        c[i]=c[i-1]+f[i];//在第i天教室的需求情况
        if(a[i]<c[i])return 0;
    }
    return 1;
}
signed main() {
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=m;i++)
    {
        cin>>b[i]>>s[i]>>t[i];
    }
    r=m,l=1;
    if(check(m))
    {
        cout<<0;
        return 0;
    }
    while(l<r)
    {
        int mid=l+r>>1;
        if(check(mid))l=mid+1;
        else r=mid;
    }
        cout<<-1<<endl;
        cout<<l<<endl;
    return 0;
}

5.K-th Number(如果第m大的数为mid,则包含mid的且mid在其中小于第k大的数的子数组数量应该小于m,若大于就是mid取小了)

查看代码
 #include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,m;
int a[1000000],b[1000000];
bool check(int mid)
{
    memset(b,0,sizeof b);
    int sum=0,t=0;
    for(int i=1;i<=n;i++)b[i]=b[i-1]+(a[i]>=mid);//前缀和求当前位置比mid大于等于的数量
    for(int i=k;i<=n;i++)
    {
        while(b[i]-b[t]>=k)t++;//固定后端,移动前端的方法算后端为b[i]时满足条件的子数组数量
        sum+=t;//t不清除因为b[i]只会越来越大
    }
    return sum>=m;//大于等于说明mid取小了
}
signed main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>k>>m;
        int l=0,r=0;
        for(int i=1;i<=n;i++)cin>>a[i],r=max(a[i],r);
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(check(mid))l=mid;
            else r=mid-1;
        }
       cout<<r<<endl;
    }
}

如果能判断出是单峰函数,就可以三分

1.传送带(三分套三分,先假定一个在线段ab上的点,得出一个单峰函数,再嵌套推剩下的cd上的点,依旧是一个单峰函数)

查看代码
 #include<bits/stdc++.h>
using namespace std;
//#define int long long
struct biao{
    double x;
    double y;
};
struct biao a,b,c,d;
int p,q,r;
double ju(biao aa,biao bb)
{
    return sqrt((aa.x-bb.x)*(aa.x-bb.x)+(aa.y-bb.y)*(aa.y-bb.y));
}
double check2(biao ab,double x)
{
    biao cd;
    cd.x=c.x+(d.x-c.x)*x;
    cd.y=c.y+(d.y-c.y)*x;
    return ju(ab,cd)/r+ju(cd,d)/q;
}
double check1(double x)
{
    biao ab;
    ab.x=a.x+(b.x-a.x)*x;
    ab.y=a.y+(b.y-a.y)*x;
    double l2=0,r2=1;
    while(r2-l2>=0.0000001)
    {
        double mid1=(l2*2+r2)/3.0;
        double mid2=(r2*2+l2)/3.0;
        if(check2(ab,mid1)<check2(ab,mid2))r2=mid2;
        else l2=mid1;
    }
    return check2(ab,r2)+ju(a,ab)/p;
}
signed  main()
{
    cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y;
    cin>>p>>q>>r;
    double l1=0,r1=1;
    while(r1-l1>=0.0000001)
    {
        double mid1=(l1*2+r1)/3.0;
        double mid2=(r1*2+l1)/3.0;
        if(check1(mid1)<check1(mid2))r1=mid2;
        else l1=mid1;
    }
    printf("%0.2lf",check1(r1));
    return 0;
}

01分数规划

1.小米买东西(基础模板)

查看代码
 #include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,m;
int a[1000000],b[1000000];
double c[1000000];
bool check(double x)
{
    double sum=0;
    for(int i=1;i<=n;i++)
    {
        c[i]=b[i]-x*a[i];
    }
    sort(c+1,c+n+1,greater<double>());
    for(int i=1;i<=m;i++)
    {
        sum+=c[i];
    }
    return sum>=0;
}
signed main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
        int l=1,r=1e4+10;
        while(l<=r)
        {
            double mid=(l+r)/2;
            if(check(mid))l=mid+1;
            else r=mid-1;
        }
        cout<<(int)r<<endl;
    }
    return 0;
}

 

posted @ 2024-07-14 16:54  伊芙加登  阅读(43)  评论(0)    收藏  举报