力扣刷題——2398. 預算內的最多機器人數目

SuzumiyaYui發表於2024-09-13

由題目中求“最多可以連續執行的機器人數目”可知,求的是陣列子陣列的長度,那麼就可以直接使用滑動視窗求解。配合字首和,可以快速的求得滑動視窗內的執行時間和。那麼編寫程式碼如下:

int maximumRobots(vector<int> &chargeTimes, vector<int> &runningCosts, long long budget)
{
    int n = chargeTimes.size();
    vector<long long> subSum;

    subSum.emplace_back(0);
    for (int i = 0; i < n; i++)
    {
        subSum.emplace_back(subSum[i] + runningCosts[i]);
    }

    long long curCost = 0;
    int left = 0;
    int maxCharge;
    int res = 0;
    for (int right = 0; right < n; right++)
    {
        curCost = subSum[right + 1] - subSum[left];
        maxCharge = chargeTimes[left];
        for (int i = left + 1; i <= right; i++)
            maxCharge = max(maxCharge, chargeTimes[i]);
        int temRes = right - left + 1;
        curCost = curCost * temRes + maxCharge;
        if (curCost <= budget)
        {
            res = max(res, temRes);
        }
        else
        {
            left++;
        }
    }
    return res;
}

但是這樣會超時,這是因為這段程式碼,對滑動視窗內的最大充電時間的維護是一個暴力演算法,會導致演算法整體的時間複雜度到O(n^2)。可以考慮用一個有序的陣列來維護滑動視窗內的最大充電時間。比如紅黑樹,結構中最後一個元素為最大充電時間,在視窗增大縮小時,也能夠很快的進行元素的刪除和增加,那麼有以下實現:

int maximumRobots(vector<int> &chargeTimes, vector<int> &runningCosts, long long budget)
{
    int n = chargeTimes.size();
    vector<long long> subSum;
    map<int, int> mp;
    subSum.emplace_back(0);
    for (int i = 0; i < n; i++)
    {
        subSum.emplace_back(subSum[i] + runningCosts[i]);
    }

    long long curCost = 0;
    int left = 0;
    int res = 0;
    for (int right = 0; right < n; right++)
    {
        curCost = subSum[right + 1] - subSum[left];
        mp[chargeTimes[right]]++;
        int temRes = right - left + 1;
        curCost = curCost * temRes + (--mp.end())->first;
        if (curCost <= budget)
        {
            res = max(res, temRes);
        }
        else
        {
            if (--mp[chargeTimes[left]] == 0)
                mp.erase(chargeTimes[left]);
            left++;
        }
    }
    return res;
}

用紅黑樹的方法很好理解,但是有點殺雞用牛刀的意思。。。實現還有最佳化空間,可以考慮用一個雙端佇列實現單調佇列,以此維護視窗內的最大充電時間,佇列的首個元素為最大充電時間。在視窗右拓的時候,可以從後往前捨棄佇列中的元素;而在視窗左縮的時候,可以從前往後彈出元素。在佇列中存入最大充電時間的下標,將更加容易實現這個程式碼:

int maximumRobots(vector<int> &chargeTimes, vector<int> &runningCosts, long long budget)
{
    int n = chargeTimes.size();
    vector<long long> subSum;
    deque<int> dq;
    subSum.emplace_back(0);
    for (int i = 0; i < n; i++)
    {
        subSum.emplace_back(subSum[i] + runningCosts[i]);
    }

    long long curCost = 0;
    int left = 0;
    int res = 0;
    for (int right = 0; right < n; right++)
    {
        while (!dq.empty() && chargeTimes[dq.back()] <= chargeTimes[right])
            dq.pop_back();
        dq.push_back(right);

        while (!dq.empty() && (subSum[right + 1] - subSum[left]) * (right - left + 1) + chargeTimes[dq.front()] > budget)
        {
            if (dq.front() == left)
                dq.pop_front();
            left++;
        }

        if (!dq.empty())
            res = max(res, right - left + 1);
    }
    return res;
}

相關文章