二分法除了可以進行有序查找、解方程等外,還可以用來解決一些實際問題。這些問題中,非常典型的應用就是“最小化最大值問題”和“最大化最小值問題”
“最小化最大值問題”和“最大化最小值問題”在優化問題中比較常見,簡單來說,“最小化最大值”是為了壓制優化目標中表現最突出的成分,“最大化最小值”為了提升優化目標中表現最差的成分。
(1)“最小化最大值問題”
一般來說,優化時考慮的是目標函數的最大化或最小化的問題。但是在某些情況下,則要求最大值的最小化才有意義。例如,在城市規划中需要確定急救中心、消防中心的位置,可取的目標函數應該是到所有地點最大距離的最小值(即急救中心、消防中心的建設位置應保證它到最遠需求點的距離盡可能小),而不是到達所有目的地距離和的最小值。因為城市同時發生事故或同時着火的幾率極低,因此更多應該考慮如何降低最惡劣情況的影響,即使是最遠的地方出事了,中心到它們的距離也能達到最小。
(2)“最大化最小值問題”
這個問題在通信鏈路中應用比較多,如基站同時和多用戶通信,每個基站到用戶的通信為一個通信鏈路,且基站的發射功率是固定的。為了保證所有的通信鏈路都正常工作,應該去優化最差鏈路的通信情況,降低信道較好鏈路的基站發射功率,增加信道較差鏈路的基站發射功率,這是一個最大化最小值問題。
【例1】數列分段。
題目描述
對於給定的一個長度為N的正整數數列A-i,現要將其分成M(M≤N)段,並要求每段連續,且每段和的最大值最小。
關於最大值最小:
例如一數列4 2 4 5 1要分成3段
將其如下分段:
[4 2] [4 5] [1]
第1段和為6,第2段和為9,第3段和為1,和最大值為9。
將其如下分段:
[4] [2 4] [5 1]
第1段和為4,第2段和為6,第3段和為6,和最大值為6。
並且無論如何分段,最大值不會小於6。
所以可以得到要將數列4 2 4 5 1要分成3段,每段和的最大值最小為6。
輸入輸出格式
輸入格式:
第1行包含兩個正整數N,M(N≤100000,M≤N)。
第2行包含N個空格隔開的非負整數Ai(Ai之和不超過10^9)
輸出格式:
一個正整數,即每段和最大值最小為多少。
輸入輸出樣例
輸入樣例#1:
5 3
4 2 4 5 1
輸出樣例#1:
6
(1)編程思路。
要解決這個最小化最大值的問題,基本思路就是選取任意一個范圍(輸入數組的最大值到數組所有元素的和),然后在這個范圍內進行二分法,每次把范圍的中間值mid當作最小值,然后判斷在mid值下數組是否能夠被分為m個部分。如果判斷出值為mid時可以將數組分成m個部分,就先讓mid變大再試試,即增大下界(left=mid+1);如果分不成m個部分,說明當前的mid太大了,就先讓mid變小再進行判斷,即減小上界(right=mid)。直到求出一個最大的mid就是最終的答案。
(2)源程序。
#include <stdio.h>
int n,m;
int a[100000];
bool judge(int mid)
{
int sum=0;
int count=0;
for(int i=0;i<n;i++)
{
sum += a[i];
if(sum>mid)
{
sum=a[i];
count++;
}
}
if ((count+1)<=m)
return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
int left=-1,right=0,mid,i;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
right += a[i];
if(a[i]>left)
{
left = a[i];
}
}
while(left<right)
{
mid=(left+right)/2;
if(judge(mid))
{
right = mid;
}
else
{
left = mid+1;
}
}
printf("%d\n",left);
return 0;
}