二分

1、二分

C++ STL的二分查找函數:ios

binary_search  返回bool值,是否存在。c++

lower_bound  返回可插入的最小值的迭代器,即返回第一個符合條件的元素位置。(從已排好序的序列a中利用二分搜索,找出ai>=k的ai的最小指針)git

upper_bound  返回可插入的最大位置的迭代器,即返回最後一個符合條件的元素位置。(ai>k)promise

例:CF706Bide

/*binary_search本身第一次寫的*/
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <string> #include <cstdlib> using namespace std; const int maxn = 100005; int a[maxn],b[maxn],m; int binary(int x) { int low=0,hight=m-1; int mid; mid=(hight-low)/2+low; while(low<=hight) { if(x==a[mid]&&a[mid+1]!=a[mid]) return mid+1; else if(x<a[mid]) hight=mid-1; else low=mid+1; mid=(hight-low)/2+low; } } int main() { int i,q; //freopen("Atext.in","r",stdin); cin >> m; for(i=0;i<m;i++) cin >> a[i]; cin >> q; sort(a,a+m); for(i=0;i<q;i++) cin >> b[i]; for(i=0;i<q;i++) { if(b[i]>=a[m-1]) cout << m << endl; else cout << binary(b[i]) << endl; } return 0; }

1.其中,若是尋找的value存在,那麼lower_bound返回一個迭代器指向其中第一個這個元素。upper_bound返回一個迭代器指向其中最後一個這個元素的下一個位置(明確點說就是返回在不破壞順序的狀況下,可插入value的最後一個位置)。若是尋找的value不存在,那麼lower_bound和upper_bound都返回「假設這樣的元素存在時應該出現的位置」。(= =槓掉的部分是我看了個假視頻)。函數lower_bound()在first和last中的前閉後開區間進行二分查找,返回大於或等於val的第一個元素位置。若是全部元素都小於val,則返回last的位置,且last的位置是越界的!!要指出的是lower_bound和upper_bound在源碼中只是變換了if--else語句斷定條件的順序,就產生了最終迭代器位置不一樣的效果。函數

2.binary_serach試圖在已排序的[first,last)中尋找元素value,若存在就返回ture,若不存在則返回false。返回單純的布爾值也許不能知足需求,而lower_bound,upper_bound能提供額外的信息。事實上由源碼可知,binary_serach即是利用lower_bound求出元素應該出現的位置,而後再比較該位置的值與value值。該函數有兩個版本一個是operator<,另一個是利用仿函數comp進行比較。this

/*p是嚴格不重複單調遞增的序列
  q是小明猜的數字,隨機有重複
  對每個q在p中進行查找,找到相同的就記錄該值
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#define maxn 1000005

using namespace std;
int p[maxn],q[maxn];
int main()
{
    long long n,m,i,sum=0;
    memset(p,0,sizeof(p));
    memset(q,0,sizeof(q));
    cin >> n >> m;
    for(i=1;i<=n;i++)         //從1開始,由於lower_bound與upper_bound必定會返回一個值,防止查找不到時越界
        cin >> p[i];
    for(i=1;i<=m;i++)                             //O(n)
    {
        cin >> q[i];
        if(q[i]==p[lower_bound(p,p+n,q[i])-p])   //O(logn)
            sum+=q[i];
    }
    cout << sum << endl;
    return 0;
}
/*查找x在一個數列a中重複出現的次數*/
#include <iostream>
#include <algorithm>
#include <cstring>
#define maxn 100005

using namespace std;
int a[maxn];
int main()
{
    int n,m,Q,i;
    while(cin >> n >> m)
    {
        for(i=1;i<=n;i++)
            cin >> a[i];
        sort(a,a+n);
        for(i=0;i<m;i++)
        {
            cin >> Q;
            int k=upper_bound(a+1,a+n+1,Q)-lower_bound(a+1,a+1+n,Q);
            cout << k << endl;
        }
    }
    return 0;
} 

3.應用:二分答案+檢驗。對於難以直接肯定解的問題,採起二分枚舉+檢驗的思想將求解類問題轉換爲驗證類問題。spa

 例:POJ1064  http://poj.org/problem?id=1064指針

題目大意:有n條繩子,它們的長度爲Li,從它們中切割出k條相等長度繩子,這k條繩子每條最長能有多長;code

像這樣在求解最大化或最小化問題中,可以比較簡單的判斷條件是否知足,那麼使用二分枚舉答案就能夠很好的解決問題;

Cable master

Description

Inhabitants of the Wonderland have decided to hold a regional programming contest. The Judging Committee has volunteered and has promised to organize the most honest contest ever. It was decided to connect computers for the contestants using a "star" topology - i.e. connect them all to a single central hub. To organize a truly honest contest, the Head of the Judging Committee has decreed to place all contestants evenly around the hub on an equal distance from it. 
To buy network cables, the Judging Committee has contacted a local network solutions provider with a request to sell for them a specified number of cables with equal lengths. The Judging Committee wants the cables to be as long as possible to sit contestants as far from each other as possible. 
The Cable Master of the company was assigned to the task. He knows the length of each cable in the stock up to a centimeter,and he can cut them with a centimeter precision being told the length of the pieces he must cut. However, this time, the length is not known and the Cable Master is completely puzzled. 
You are to help the Cable Master, by writing a program that will determine the maximal possible length of a cable piece that can be cut from the cables in the stock, to get the specified number of pieces.

Input

The first line of the input file contains two integer numb ers N and K, separated by a space. N (1 = N = 10000) is the number of cables in the stock, and K (1 = K = 10000) is the number of requested pieces. The first line is followed by N lines with one number per line, that specify the length of each cable in the stock in meters. All cables are at least 1 meter and at most 100 kilometers in length. All lengths in the input file are written with a centimeter precision, with exactly two digits after a decimal point.

Output

Write to the output file the maximal length (in meters) of the pieces that Cable Master may cut from the cables in the stock to get the requested number of pieces. The number must be written with a centimeter precision, with exactly two digits after a decimal point. 
If it is not possible to cut the requested number of pieces each one being at least one centimeter long, then the output file must contain the single number "0.00" (without quotes).

Sample Input

4 11
8.02
7.43
4.57
5.39

Sample Output

2.00

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double eps=0.0001;
const int maxn=10005;
int n,k;
double L[maxn];
bool cc(double x)           //對於枚舉的x的長度,判斷其是否知足k個的要求;
{
    int cnt=0;
    for(int i=0;i<n;i++)
        cnt+=(int)(L[i]/x);
    return cnt>=k;
}
void solve()
{
    double l=0,r=200005;    //題目是1m--100km,這裏又WA了一回;
    while(r-l>=eps){        //能夠把終止條件設爲一個指定的區間大小,也能夠設爲循環次數,但注意不要由於精度太高而死循環;
        double mid=(l+r)/2.0;
        if(cc(mid))l=mid;
        else    r=mid;
    }
    //printf("%.2lf\n",r);              感受這裏應該有浮點偏差,不加floor函數的不到正確值
    printf("%.2lf\n",floor(r*100)/100); //幸虧樣例給的好,否則WA了,徹底找不出來0.0;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%lf",&L[i]);
    solve();
    return 0;
}

最大化平均值,01規劃,最大化最小值,最小化最大值。

 例:http://codeforces.com/contest/626/problem/C

#include <bits/stdc++.h>

using namespace std;
//這二分用的太精緻了,妙呀QAQ;
int n,m;
bool check(int x)//二分的斷定基本都是用貪心去check;
{
    int num1=x/2;
    int num2=x/3;
    int num3=x/6;
    if(num1<n)return false;
    if(num2<m)return false;
    if(min(num3,num1-n)<m-(num2-num3))return false;//判斷2,3的公倍數儘可能知足是否成立;
    else return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    int l=0,r=1e7,ans=0;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid))ans=mid,r=mid-1;
        else  l=mid+1;
    }
    cout << ans << endl;
    return 0;
}
相關文章
相關標籤/搜索