1. 程式人生 > >TOJ 2789.Monthly Expense(二分經典題目)

TOJ 2789.Monthly Expense(二分經典題目)

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.
………….

這個題對於像我這種的英語渣來說簡直是噩夢,看著書上的解釋才算是徹底的明白題意。其實很簡單,說白了就是求一個複合函式的最小值。什麼養的符合函式呢?就是將n天分為m組,每組包含連續的一天或幾天,若第i組的花費為k,則求k=max{ki}的最小值。這道題演算法很簡單,就是純粹的二分法,寫起來也就短短的幾十行。但是這道題正真經典的地方在於你很難想到可以用二分,你也想不到該怎麼二分。實際上這道題可抽象為查詢一個函式的某一個,而且是一個在某種意義上說是單調函式的查詢,因此可以用二分(如果是凸函式則要用到三分)。接下來怎麼二分就是這道題比較巧妙的地方了,可以以n天中的最大花費作為low,總和作為high,這樣這道題就變成了從區間[ low , high ]中找一個值,使這個值滿足題目要求即可,最後還需要提一點:為了使找到的值是最小的,用了一點貪心的思想,儘可能的使每一組的k接近當前的K值,這樣就能是找到的滿足條件的K值是最小的。

  • 提交時超時了一次,起先非常鬱悶:想不到這道題怎麼會超時,最後發現把while(~scanf("%d%d",&n,&m))寫成了while(scanf("%d%d",&n,&m)),這樣就導致出入死在了while裡,不超時就有鬼了。。。
#include <stdio.h>
#include <cmath>
#include <iostream>
using namespace std;
int a[100002];
int n,m;
bool find(int slove){
    int num=1;
    int sum=0
; for(int i=0;i<n;i++) if(sum+a[i]<=slove) sum+=a[i]; else{ num++; sum=a[i]; } return (num<=m); } int main(){ while(~scanf("%d%d",&n,&m)){ int low=0,high=0,mid; for(int i=0;i<n;i++){ scanf("%d",&a[i]); high+=a[i]; low=max(low,a[i]); } while(low!=high){ mid = (low+high)>>1; if(find(mid)) high = mid; else low=mid+1; } printf("%d\n",low); } }