ACM-二分-三分查找笔记

思想: 分治。
适用范围:二分只适用于单调函数,对单调递增或单调递减的一个序列中的某一个元素进行查找;三分用于凸函数和凹函数。
复杂度分析:二分的时间复杂度为log2(n),而三分的时间复杂度为3log3(n)。

<<挑战程序设计竞赛>>

3.1.2 假定一个解并判断是否可行。

Poj1064 - Cable master

题意:给出n条绳子,长度分别为Li,裁剪出m条等长且尽量长的线段,并且让这些线段尽可能长。

#include
#include
using namespace std;
#define N 11000
#define INF 1000000
double a[N];
int n, k;
double C(double x)
{
    int num = 0;
    for (int i = 0; i < n; i++) num += (int) (a[i] / x);
    return num >= k;
}
void solve()
{
    double l = 0;
    double r = INF;
    for (int i = 1; i < 100; i++){
        double mid = (l+r)*1.0 / 2;
        if (C(mid)) l = mid;
        else r = mid;

    }
    printf("%.2f\n",floor(l*100)/100);
}
int main ()
{
    //yyy_3y
    freopen("1.in","r",stdin);
    scanf("%d %d",&n,&k);
    for (int i = 0; i < n; i++)  scanf("%lf",&a[i]);
    solve();
}

3.1.3、 二分最大化最小值

Poj2456 - Aggressive cow

题意:有n个牛舍,第i个牛舍在xi的位置上面,但是m头牛会互相攻击,因此要使得最近的两头牛之间的距离尽量大,求这个距离。
思路: 1:对牛舍的位置x进行排序
2:把第一头牛放入X0 的牛舍
3.如果第i头牛放入了xj的话,第i+1头牛就要放入满足
xj xk 的最小xk中。

#include
#include
#include
using namespace std;
#define N 110000
#define INF 1000000010
int a[N];
int n, c;
bool C(int d)
{
    int last = 0;
    for (int i = 1; i < c; i++){
        int crt = last + 1;

        while (crt < n && a[crt] - a[last] < d) crt++;
  //       printf("i = %d ,cnt = %d\n",i,crt);
        if (crt == n) return false;
        last = crt;
    }
    return true;
}
void solve()
{
    sort(a,a+n);
    int l = 0;
    int r = INF;
    while(r - l > 1){
        int mid = (l+r) / 2;
        if (C(mid)) l = mid;
        else r = mid;
    }
    printf("%d\n",l);
}
int main ()
{
    //yyy_3y
  //  freopen("1.in","r",stdin);
    scanf("%d %d",&n,&c);
    for (int i = 0; i < n; i++)  scanf("%d",&a[i]);
    solve();
    return 0;
}


补充

3.1.4 最大化平均值

Poj2976 - Dropping tests

题意:一共有N场考试,每场一共有b题,每场对a题,我们可以去掉k场的成绩,使得最后的正确率最大。
思路:二分正确率。

#include
#include
#include 
#include 
using namespace std;
#define eps 1e-6
const int N = 1010;
int n,k;
double x;
struct node{
    int a,b;
    bool operator <(const node & c) const{
        return a-x*b < c.a-x*c.b;
    }
}T[N];
bool C(double mid)
{
    x=mid;
    sort(T+1,T+1+n);
    double a=0.0,b=0.0;
    for(int i=k+1;i<=n;i++){
        a+=T[i].a;
        b+=T[i].b;
    }
    return 1.0*a/b>mid;
}
void solve()
{
    double l=0.0,r=1.0;
    while(r-l>=eps){
        double mid=(l+r)/2;
        if(C(mid)) l=mid;
        else r=mid;
    }
    printf("%.0f\n", 100.0*l);
}
int main ()
{
    //yyy_3y
      //  freopen("1.in","r",stdin);
    while(scanf("%d%d",&n,&k)&&n+k){
        for (int i = 1; i <= n; i++) scanf("%d",&T[i].a);
        for (int i = 1; i <= n; i++) scanf("%d",&T[i].b);
        solve();
    }
}

三分

概念:在二分查找的基础上,在右区间(或左区间)再进行一次二分,这样的查找算法称为三分查找,也就是三分法。主要来确定最值!
ACM-二分-三分查找笔记_第1张图片

1.三分半径

Poj3737 – UmBasketella

题意:给出一个圆锥的表面积, 求该圆锥的最大体积,以及此时的底面半径和高。
思路:数学公式推导,找到半径的上界和下届。三分半径即可。

#include 
#include 
#include 
#include
#define PI acos(-1.0)
#define eps 1e-6
using namespace std;
double s;
double C(double r)  
{
    double R=(s/PI/r-r);
    double h=sqrt(R*R-r*r);
    return PI*h*r*r/3;
}

double solve(double left, double right)
{
    double midl, midr;
    while (right-left > eps)
    {
        midl = (2.0*left + right) / 3;
        midr = (left + 2.0*right) / 3;
        if(C(midl) >= C(midr)) right = midr;
        else left = midl;
    }
    return left;
}
int main()
{
    while(scanf("%lf",&s)!=EOF){
        double left=0.0,right=sqrt(s/2/PI);
        left=solve(left,right);
        double R=(s/PI/left-left);
        double h=sqrt(R*R-left*left);
        double v=PI*h*left*left/3;
        printf("%.2f\n%.2f\n%.2f\n", v, h, left);
    }
    return 0;
}

2.三分再三分!

hdu 3400 – Line belt

题意:有两条传送带,传送带上有两段,AB和CD,它们的速度分别是P和Q,其它地方的速度为R,问从A到D所需要的最短时间是多少。
思路:先三分AB上的点,再三分CD上的点即可。
设X在AB上,Y在CD上。
人在线段AB上花的时间为:f(x) = AX / P,
人走完Z和Y所花的时间为:g(x) = XY / R + YD / Q。

f(x)+g(x)很明显是一个先递减后递增的函数。故可用三分法。

#include 
#include 
#include 
#include
#define PI acos(-1.0)
#define eps 1e-6
using namespace std;
struct node{
    double x,y;
}a,b,c,d,X,Y;
double p,q,r;
double dis(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double calc(double mid)
{
    Y.x=c.x+(d.x-c.x)*mid;
    Y.y=c.y+(d.y-c.y)*mid;
    return dis(Y,d)/q + dis(X,Y)/r;
}
double C(double mid)
{
    X.x=a.x+(b.x-a.x)*mid;
    X.y=a.y+(b.y-a.y)*mid;
    double l=0.0,r=1.0,midl,midr;
    double ans;
    while(r-l>eps){
        midl=(2.0*l+r)/3;
        midr=(l+2.0*r)/3;
        ans=calc(midl);
        if(ans<=calc(midr)) r=midr;
        else l=midl;
    }
    return dis(a,X)/p+ans;
}
void solve()
{
    double l=0.0,r=1.0,midl,midr;
    double ans;
    while(r-l>eps){
        midl=(2.0*l+r)/3;
        midr=(l+2.0*r)/3;
        ans=C(midl);
        if(ans<=C(midr)) r=midr;
        else l=midl;
    }
    printf("%.2f\n",C(l));
}
int main ()
{
    int t; scanf("%d",&t);
    while(t--){
        scanf("%lf%lf%lf%lf",&a.x,&a.y,&b.x,&b.y);
        scanf("%lf%lf%lf%lf",&c.x,&c.y,&d.x,&d.y);
        scanf("%lf%lf%lf",&p,&q,&r);
        solve();
    }
}

3. 三分整数类型(平均值)

codeforces — 939E. Maximize!

想到总结三分也是应为这一道题。这道题可以拿尺取法解—->尺取法
但是三分也是一种非常优秀的思路。
题意:
有两种操作。
1.往集合里面加一个数(这个数为集合中最大的数)。
2.在该集合取任一子集使得Max子集(子集中最大的元素)-AVG子集 最大

#include
#define debug(a) cout << #a << " " << a << endl
#define LL long long
#define PI acos(-1.0)
#define eps 1e-6
const int N=1e6+7;
using namespace std;
LL sum[N],x;
int cnt=0;
double ans=0;
double C(int mid)
{
    return 1.0*(sum[mid]+x)/(mid+1);
}
double solve(int l,int r)
{
    int midl,midr;
    while(r-l>2){
        midl=(2.0*l+r)/3;
        midr=(l+2.0*r)/3;
        if(C(midl)<=C(midr)) r=midr;
        else l=midl;
    }
    return min(min(C(l),C(r)),C((l+r)/2));

}
int main ()
{
    //yyy_3y
    //freopen("1.in","r",stdin);
    int n; scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int type; scanf("%d",&type);
        if(type==1){
            scanf("%lld",&x);
            sum[++cnt]=sum[cnt-1]+x;
        }
        else {
            double tmp=solve(1,cnt-1);
            ans=x-tmp;
            printf("%.10f\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(ACM-二分-三分查找笔记)