1. 程式人生 > >POJ 3273 二分求最大化最小值

POJ 3273 二分求最大化最小值

Monthly Expense
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 20086 Accepted: 7916
Description

Farmer John is an astounding accounting wizard and has realized he might run out of money to run the farm. He has already calculated and recorded the exact amount of money (1 ≤ moneyi ≤ 10,000) that he will need to spend each day over the next N (1 ≤ N ≤ 100,000) days.

FJ wants to create a budget for a sequential set of exactly M (1 ≤ M ≤ N) fiscal periods called “fajomonths”. Each of these fajomonths contains a set of 1 or more consecutive days. Every day is contained in exactly one fajomonth.

FJ’s goal is to arrange the fajomonths so as to minimize the expenses of the fajomonth with the highest spending and thus determine his monthly spending limit.

Input

Line 1: Two space-separated integers: N and M
Lines 2..N+1: Line i+1 contains the number of dollars Farmer John spends on the ith day
Output

Line 1: The smallest possible monthly limit Farmer John can afford to live with.
Sample Input

7 5
100
400
300
100
500
101
400
Sample Output

500
題意:
有n個花費,現在要分成m段。使得每一段的和最小的那個值是所有分法裡最大的。
題解:


最大化最小值或者最小化最大值的問題可以用二分法來做。為什麼用二分?
以求最大化最小值問題為例。
我們知道二分有一個前提就是對有序的東西才能二分查詢。也就是這個問題變數是有序的才能用二分。
在求最大化最小值時,既然求,那麼這個值一定有一個範圍。而求最大化最小值時。既然它可以“最大化”,那我們一般可以推到一個”最大值“。既然它可以“最小值”,那我們一般可以推到一個”最小值“。可知最終的這個“”一定在這個最大值與最小值之間的範圍裡。當我們在這個範圍裡挑一個值得時候。我們經過處理,得出一個和題目要求位置相同的變數。我們挑的這個值確定的這個變數狀態與題目的要求變數進行比較。這個就是有序的。在本題中就是,但我列舉一個總和k後,求得在這個中和下的最大分段數,這個分段數與題目要求的分m段的比較是有序的。如果比m大,說明段數分多了–>說明k列舉小了,接下來再在大於k的區間列舉。這就可以二分了。

再就是唯一性問題。
一般而言最大化最小值問題都會只有一個確定的答案(如果有多個答案,也應該都是一樣的值)這是顯然的。那麼在二分過程中我們應該是不斷縮小[L,R]的範圍,最終使得L==R才退出二分。這樣才是一個唯一的結果。如果當我們過程中查到了某個值的狀態滿足題目要求就退出。我們就無法保證這是唯一的那個答案。

同類題有POJ3258 POJ1905 POJ3122

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#define f(i,a,b) for(i = a;i<=b;i++)
#define fi(i,a,b) for(i = a;i>=b;i--)

using namespace std;
#define SIZE 100000
typedef long long ll;

ll a[SIZE+10];
int n,m;

int Cnt(ll M){
    int sum = 0,cnt = 1;
    int i;
    f(i,1,n){
        sum+=a[i];
        if(sum>M){
            cnt++;
            sum = a[i];
        }
    }
    return cnt;
}

int main()
{
    while(~scanf("%d%d",&n,&m)){
        int i;
        ll sum = 0,Max = -0x3f3f3f3f;
        f(i,1,n){
            scanf("%lld",a+i);
            sum+=a[i];
            if(a[i]>Max) Max = a[i];
        }

        ll L = Max,R = sum,M;
        while(L<=R){
            M = (L+R)/2;
            int k = Cnt(M);
            if(k<=m){
                R = M-1;
            }else
                L = M+1;
        }
        printf("%lld\n",M);
    }
    return 0;
}