1.11--06:月度開銷

落/花/桂/發表於2024-11-15

月度開銷

題目傳送門

思路

給定連續N天的開銷,需要將這些天分成M個財政週期,使得開銷最多的財政週期的開銷儘可能少。

首先,我們可以確定一個財政週期的長度l,即將N天平均分成M個財政週期。這樣每個財政週期的長度就是N/M。

然後,我們需要計算每個財政週期中的開銷總和。假設當前財政週期的起始位置為i,那麼當前財政週期的開銷總和就是從第i天開始的連續N/M天的開銷之和,即SUM(i, i+N/M-1)。

接下來,我們需要找到開銷總和最大的財政週期,即找到使得SUM(i, i+N/M-1)最大的i。

我們可以使用滑動視窗的思想來解決這個問題。首先,我們計算起始位置為0的財政週期的開銷總和SUM(0, N/M-1)。然後,我們從左往右依次移動起始位置i,每次移動一個位置,將起始位置i-1的開銷減去第i-1天的開銷,再加上第i+N/M-1天的開銷,即得到起始位置為i的財政週期的開銷總和SUM(i, i+N/M-1)。我們將SUM(i, i+N/M-1)與原先的最大開銷進行比較,如果大於最大開銷,就更新最大開銷。

最後,最大開銷就是最大月度開銷的最小值。

#include <bits/stdc++.h>
using namespace std;
#define N 100005 
int n, m, a[N];
bool check(int k) //在每個子段和小於等於k的情況下,最少的子段數量是否小於等於m
{//注意:a中的單獨一個元素也可能大於k 
    int sum = 0, ct = 1;//sum:當前子段加和 ct:現在在看第幾個子段 
    for(int i = 1; i <= n; ++i)
    {
        if(a[i] > k)//存在元素大於k, 
            return false;
        if(sum + a[i] <= k)
            sum += a[i];
        else    
        {
            ct++;//看下一子段 
            sum = a[i];//i作為下一子段的第一個元素 
        }
    }
    return ct <= m;    
}
int main()
{
    int tot = 0;//加和 
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        tot += a[i];
    }
    int l = 0, r = tot, mid;//子段和最大值不會大過所有數的加和 
    while(l < r)//二分答案求滿足某一條件的最小值 
    {
        mid = (l + r) / 2;
        if(check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l;
    return 0;
}

相關文章