1. 程式人生 > >二分最化最值問題(一)

二分最化最值問題(一)

一個人 子序列 兩個 ace ffffff con false std 算法

二分最化最值問題

把一個包含n個正整數的序列劃分成m個連續的子序列(每個正整數恰好屬於一個序列).

設i個序列的各數之和為s(i).你的任務是讓所有s(i)的最大值盡量小.

這個算法很有現實意義.給你一堆亂七八糟的東西分堆,分出來的東西都不超過一個固定的數值,這是需要技術含量的喔.



1.首先確定二分的上下界,這是每次二分必須做的準備工作.在這裏上限是這一堆東西的總量,下限是單個最大物品的值.


2.確定上下限之後開始二分.即判斷當前分堆是否合理,再判斷分堆是否合理中,主要限制因素為兩個,一個是單堆的量肯定不能超過當前的mid值,另一個是分出的堆數一定不能超過題目要求的m值.


3.由判斷條件即可將二分範圍進行縮小,即一旦當前mid值滿足分堆要求

,意味著我還可以把mid值縮小(最小化),即把二分的right=mid,如果當前mid值觸犯了判斷條件,就把left=mid.


4.由以上二分return
的結果即為所求值.

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int ok(int m)
{
    int sum=0,cnt=0;
    for(int i=0;i<n;i++)
    {
        if(a[i] > m) return false;
        
        if(sum + a[i] > m)
        {
            cnt++;
            sum = a[i];
        }
        else sum+=a[i];
    }
    return cnt <= m;
}
int main()
{
    int n,k,sum=0;
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        sum+=a[i];          //r
        Max=max(Max,a[i);   //l
    }
    int l=Max,r=sum;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(ok(mid)) r=mid;
        else l=mid+1;
    }
    cout<<l<<endl;
}

最小化最大值


和最大化最小值問題正好相反,同樣用二分解決,差別主要在判斷條件.


1.最大化最小值(兼濟天下)

相當於把n個東西分給m個人,使得每個人至少拿x個,那麽每個人拿夠了就走,給後面的人多留一點,只要能分夠>=m個人就是true,多的全扔給最後一個人就是了.

2.最小化最大值(獨善其身)


相當於n個東西分給m個人,每個人至多拿x個 ,那麽每個人盡可能拿多一點,給後面的人少留一點,只要能使<=m個人分完n個東西就是true,之後每個人隨便拿一點給沒有拿到的人就是了.


註意:


1.關於初始值,有些題目直接給前綴和,那樣最小值的最大值就是平均值,最大值的最小值也是平均值

對於可以計算sum的題有:

2.最大化最小值:

x為單點最小值,y為平均值.

3.最小化最大值:

x為平均值與單點最大值兩者中大的那一個,y為sum.其實直接令x=0,y=0x7fffffff即可,不過循環32次而已...

二分最化最值問題(一)