ACM-二分-三分查詢筆記

yyy_3y發表於2018-02-22

思想: 分治。
適用範圍:二分只適用於單調函式,對單調遞增或單調遞減的一個序列中的某一個元素進行查詢;三分用於凸函式和凹函式。
複雜度分析:二分的時間複雜度為log2(n),而三分的時間複雜度為3log3(n)。

<<挑戰程式設計競賽>>

3.1.2 假定一個解並判斷是否可行。

Poj1064 - Cable master

題意:給出n條繩子,長度分別為Li,裁剪出m條等長且儘量長的線段,並且讓這些線段儘可能長。

#include<cstdio>
#include<math.h>
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頭牛就要放入滿足

x
xj
\leq
xk
的最小x
的最小xk中。

#include<cstdio>
#include<math.h>
#include<algorithm>
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<cstdio>
#include<math.h>
#include <algorithm>
#include <cstring>
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();
    }
}

三分

概念:在二分查詢的基礎上,在右區間(或左區間)再進行一次二分,這樣的查詢演算法稱為三分查詢,也就是三分法。主要來確定最值!
這裡寫圖片描述

1.三分半徑

Poj3737 – UmBasketella

題意:給出一個圓錐的表面積, 求該圓錐的最大體積,以及此時的底面半徑和高。
思路:數學公式推導,找到半徑的上界和下屆。三分半徑即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include<cmath>
#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 <iostream>
#include <algorithm>
#include <cstdio>
#include<cmath>
#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<bits/stdc++.h>
#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;
}

相關文章