【一本通OJ 1603:绿色通道】题解
题目链接
题目
高二数学《绿色通道》总共有 \(n\) 道题目要抄,编号 \(1\ldots n\),抄第 \(i\) 题要花 \(a_i\) 分钟。小 Y 决定只用不超过 \(t\) 分钟抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒,最长的空题段越长,马老师越生气。
现在,小 Y 想知道他在这 \(t\) 分钟内写哪些题,才能够尽量减轻马老师的怒火。由于小 Y 很聪明,你只要告诉他最长的空题段至少有多长就可以了,不需输出方案。
思路
由于最长长度和最短时间都不确定。我们可以假设其中一项确定来思考。
考虑二分答案,二分最长空题段。
假设二分当前最长空题段为 \(k\),我们就可以尝试dp处理。
设 \(dp_i\) 表示第 \(i\) 题做,且前 \(i\) 题的最长空题段小于等于 \(k\) 时的最短时间,我们可以枚举上一条做了的题,即:
\[dp_i=\min_{\max(j=i-k-1, 0)}^{i-1}dp_j+a_i
\]
答案我们可以在 \([n-k-1, n]\) 里枚举最后一道题的位置,时间复杂度 \(O(n^2\log n)\)。
显然,\(\min_{\max(j=i-k-1, 0)}^{i-1}dp_j\) 可以用单调队列优化,时间复杂度 \(O(n\log n)\)。
总结
这是一道二分+单调dp的模板。
题目问最长能多长,可以考虑二分,成为切入点。
当最长长度确定,dp也很容易可以写出。
超时后也容易发现,式子中可以用单调队列优化,代码不难打。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 500010
int n, m, i, j, k;
int l, r, mid, ans;
int dp[N], a[N], t;
pair<int , int>p;
deque<pair<int, int> >q;
int check(int k) {
q.clear();
p.first=0; p.second=0;
q.push_back(p);
for(i=1; i<=n; ++i) {
dp[i]=q.front().first+a[i];
while(!q.empty() && i-q.front().second>k) q.pop_front();
while(!q.empty() && dp[i]<=q.back().first) q.pop_back();
p.first=dp[i];
p.second=i;
q.push_back(p);
}
ans=0x7fffffffffffffff;
for(i=n-k-1; i<=n; ++i) ans=min(ans, dp[i]);
return ans<=t;
}
signed main() {
scanf("%lld%lld", &n, &t);
for(i=1; i<=n; ++i) scanf("%lld", &a[i]);
l=1, r=n;
while(l<r) {
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%lld", l);
return 0;
}
本文来自博客园,作者:zhangtingxi,转载请注明原文链接:https://www.cnblogs.com/zhangtingxi/p/15897506.html