题解【AcWing1090】绿色通道

题面

题目要求出最长的空题段最短的长度,显然可以二分答案。

考虑如何 check。

设二分到的值是 (x),即最长的空题段长度至少为 (x)

其实整个 check 的过程可以看作一个 DP,设 (dp_i) 表示 (1sim i) 中 写第 (i) 道题 且 最长的空题段长度 (leq x) 所需的最短时间。

由于空题段最长为 (x),且区间 ([i-x, i - 1]) 中有 (x) 个点, 所以转移方程不难得出:(dp_i=min_{i-x-1leq j leq i-1}{dp_j} + a_i)

问题又变成了一个典型的滑动窗口问题,可以使用单调队列优化。

#include <bits/stdc++.h>

using namespace std;

const int N = 50003;

int n, m, t, a[N], dp[N], q[N], hh, tt, ans;

inline bool check(int x)
{
    memset(dp, 0, sizeof dp);
    hh = tt = 0; //队列中预先存储了一个 dp[0] = 0
    for (int i = 1; i <= n; i+=1)
    {
        while (hh <= tt && q[hh] < i - x - 1) ++hh; //队头超出了范围
        dp[i] = dp[q[hh]] + a[i]; //转移
        while (hh <= tt && dp[q[tt]] >= dp[i]) --tt; //维护队列单调性
        q[++tt] = i; //加入队列
    }
    int sum = 2000000007; 
    for (int i = n - x; i <= n; i+=1)
        sum = min(sum, dp[i]); //求出最短的时间
    return sum <= t; //最多只有 t 分钟
}

int main()
{
    cin >> n >> t;
    for (int i = 1; i <= n; i+=1) cin >> a[i];
    int l = 0, r = n;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/xsl19/p/12362748.html