POJ 題解 | 3273.Monthly Expense —— 最大值最小化 二分答案 C++

寫在前面:二分答案類型的題目有固定的套路,學會以後這類題就能迎刃而解。但要注意認真對待,記錄下曾踩過的坑,以免日後再犯(呵呵,將來一定還會錯

題目描述

原題鏈接
在這裏插入圖片描述

算法

(二分答案) O(nlogn)O(nlogn)

最大值的最小化問題

			Max
15 52 33-> 52
10 50 30 -> 50
30 30 30 -> 30

二分模型:找到第一個滿足要求的數(右半段)

二分答案套路:先找出答案可能的區間,然後判斷答案是否滿足條件,根據條件對區間進行二分

注意:確定區間時如果精確的去求可能的範圍,那check函數不用加額外判斷;如果是隨意擴大了範圍,那x如果很小,小於num[i],那它必然不是最大值,所以此時必須直接返回false(我的check函數寫法的原因)

Debug:

  1. 一開始m沒有傳到check裏面,我以爲m是全就不用穿了,可是這樣會導致m的值在下一輪會改變。
  2. 做這個題還想到了101 和 1900 二分不是會有小數嗎,原來l + r + 1就是用來解決這個的。
  3. 一個月沒刷題了,有點忘了自頂向下做題步驟,求答案區間範圍時max居然寫成了min。其實求最大範圍時,直接設一個無窮大值即可
  4. 不管怎麼樣,check函數一開始一定要m–,即一定要用一天,然後如果超過x了,說明還要再用一天,這是二分答案的套路

Saturday, May 9, 2020 14:47:37
更新:建議還是求出區間的精確範圍,因爲check函數那個判斷條件很容易忘記寫

時間複雜度是O(nlogn)O(nlogn),空間複雜度是O(n)O(n)

C++代碼

#include <iostream>

using namespace std;

typedef long long LL;

const int N = 1e5 + 7;
int n, m;
LL a[N];
LL l = 0, r = 0;

bool check(int x, int m) {
    LL sum = 0;
    m --; // 一開始一定要用1天
    // 2 4 | 3 2 | 3  (m = 7)
    for (int i = 0; i < n; i ++) {
        sum += a[i];
        if (sum > x) { // 如果超過x,說明還要再用一天
            sum = a[i];
            m --;
            if (a[i] > x) return false; // 如果隨意擴大了範圍,那這句話一定要寫
        }
    }
    return m >= 0;
}


int main() {
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    cin >> n >> m;

    // 找到答案可能的區間
    for (int i = 0; i < n; i ++) {
        cin >> a[i];
        // r += a[i];
        // l = max(l, a[i]);
    }

    l = -2e9, r = 2e9; // 用無窮大設範圍是可以的,但要注意check函數
    while (l < r) {
        LL mid = (l + r) >> 1;
        if (check(mid, m)) r = mid;
        else l = mid + 1;
    }

    cout << r << endl;

    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章