二分法(三):采用二分法解決“最大化最小值問題”


【例1】跳石頭。

題目描述

一年一度的跳石頭比賽又要開始了!

這項比賽將在一條筆直的河道中進行,河道中分布着一些巨大岩石。組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 N 塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。

為了提高比賽難度,組委會計划移走一些岩石,使得選手們在比賽過程中的最短跳躍距離盡可能長。由於預算限制,組委會至多從起點和終點之間移走 MM 塊岩石(不能移走起點和終點的岩石)。

輸入輸出格式

輸入格式:

第一行包含三個整數 L,N,M,分別表示起點到終點的距離,起點和終點之間的岩石數,以及組委會至多移走的岩石數。保證0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。。

接下來 N 行,每行一個整數,第 i 行的整數 D_i( 0 < D_i < L), 表示第 i 塊岩石與起點的距離。這些岩石按與起點距離從小到大的順序給出,且不會有兩個岩石出現在同一個位置。

輸出格式:

一個整數,即最短跳躍距離的最大值。

輸入輸出樣例

輸入樣例#1:

25 5 2

2

11

14

17

21

輸出樣例#1:

4

說明

輸入輸出樣例 1 說明:將與起點距離為 2和 14的兩個岩石移走后,最短的跳躍距離為 4(從與起點距離 17的岩石跳到距離 21的岩石,或者從距離 21的岩石跳到終點。

      (1)編程思路。

      這道題要求一堆最小距離里面的最大值,是一道典型的最小值最大化問題,可以采用二分法解決。

      1)首先將距離排序,雖然石子順序是確定的,但是排完序不影響它們之間的差值,這個是肯定的。

      2)確立二分的上下界,上界right就是河流的寬度L,下界left就是最小石頭間距。

      3)然后在上下界之間二分,二分時需要確定判斷條件,測試當前的mid值。

      判斷當前mid值的方法是這樣的:循環所有的石頭間距,逐個累加,如果沒有超過當前mid,意味着該石頭可以搬開(這是為了保證最小跳躍長度為mid,小於mid距離的石頭不能往上面跳,因為如果一跳就會出現比mid更小的距離了),即搬石頭數x++,如果超過了當前mid,則不能搬了(需要跳到這個石頭上落下腳),而且要把此時的累加距清零,以便后一段繼續如此處理。

      根據循環之后的結果,如果搬石頭數目x超過了規定的m,說明mid值過大,於是上界縮小right變為mid-1:如果x小於m,則在跳躍時還可以跳過m-x個石頭,說明mid值偏小,則下界增大left變為mid+1……由此二分完畢即得最大化最小間距。

      (2)源程序。

#include <iostream>

#include <algorithm>

using namespace std;

int d[50005],l,n,m; 

bool judge(int mid)

{

         int start=0,x=0,i; // start表示每次落腳點的坐標,每落一次地更新一次start

         for(i=1;i<=n;i++)

         {

                   if (d[i]-start<mid)

                      x++;   // x表示去掉的石頭數,如果mid大於要跳的距離,就跳過當前這個石頭,此時x++ 

                   else

                      start=d[i];  // 此時落在石頭上

         }

         if (l-start<mid)   // 判斷最后一跳跳的距離要是小於mid的話那是不可以的

             return false;

         if(x>m)          //  要是x>m就說明最小距離mid太大啦

             return false;

         return true;

}

int main()

{

     int left,right,mid,ans,min = 0x7fff,i;

     cin>>l>>n>>m;

     for(i=1;i<=n;i++)

          cin>>d[i];

     d[0] = 0; 

     d[n + 1] = l;

     sort(d,d+(n+1));

     for(i = 0; i <= n; i++) 

        if (d[i+1]-d[i]<min) 

             min = d[i+1]-d[i]; 

     left = min, right = l;

     while(left<=right)

     {

          mid=(left+right)/2;

          if (judge(mid))

           {

                     left=mid+1;

                     ans=mid;

            }

            else

                   right=mid-1;

     }

     cout<<ans<<endl;

     return 0;

}

      將此源程序提交給POJ  3258 “River Hopscotch”,可以Accepted。

 【例2】好斗的牛(POJ 2456 翻譯而來)。

 題目描述

 農夫約翰建造了一座有n(2<=n<=100000)間牛舍的小屋,牛舍排在一條直線上,第i間牛舍在xi(0<=xi<=1000000000)的位置。但是約翰的c(2<=c<=n)頭牛不喜歡牛舍這種布局,而且幾頭牛如果沖到同一個隔間里,它們就要發生爭斗。約翰為了防止牛之間互相攻擊互相傷害,因此決定把每頭牛都放在離其它牛盡量遠的牛舍,使任意兩頭牛之間的最小距離盡可能大,那么,這個最大的最小距離是多少呢?

輸入格式

第一行是用空格分隔的兩個整數n,c

第二行為n個用空格隔開的整數,表示位置xi

輸出格式

輸出僅一個整數,表示最大的最小距離值

樣例輸入

5 3

1 2 8 4 9

樣例輸出

3

樣例解釋

 把牛放在第1,4, 8 間,這樣最小的距離值是3

      (1)編程思路。

      將C頭牛放在N個點中的C個點上的最大距離是:dis=(Pmax-Pmin)/(C-1)。(最大的坐標-最小的坐標再除以C-1)。

      首先對隔間位置xi從小到大排序,然后以left=0為下界,以right=dis為上界通過二分法求這個最大的最小距離。

      假設當前的最小距離為mid,如果判斷出最小距離為mid時可以放下C頭牛,就先讓mid變大再試試,即增大下界(left=mid+1);如果放不下C頭牛,說明當前的mid太大了,就先讓mid變小再進行判斷,即減小上界(right=mid-1)。直到求出一個最大的mid就是最終的答案。

      (2)源程序。

#include <iostream>

#include <algorithm>

using namespace std;

const int N = 100005;

int p[N], n, c;

bool judge(int x)

{

    int cnt = 1, tmp = p[0];

    for(int i = 1; i < n; i++)

    {

        if(p[i] - tmp >= x)

        {

            cnt++;

            tmp = p[i];

            if(cnt >= c)    //可以放下C頭牛

                return true;

        }

    }

    return false;

}

int main()

{

    int i,low,high,mid;

    cin>>n>>c;

    for(i=0;i<n;i++)

        scanf("%d",&p[i]);

    sort(p,p+n);

    high=(p[n-1]-p[0])/(c-1);

    low=0;

    while(low<=high)

    {

         mid=(low+high)/2;

         if (judge(mid)) low=mid+1;

         else high=mid-1;

    }

   cout<<low-1<<endl;

   return 0;

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM