二分+DP+单调队列优化绿色通道 LibreOJ - 10181
Posted Vincent_0000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分+DP+单调队列优化绿色通道 LibreOJ - 10181相关的知识,希望对你有一定的参考价值。
题目来源
反思
这个题目刚开始做的时候就已经把自己扼杀在摇篮里面了,因为自己在刷单调队列优化DP嘛,看到这个题目有很明显的二分性质的时候就想:这个题目在考DP,不会考二分的,二分肯定有坑,然后就直接放弃了。然后往其他方向也没想出对应的方案出来。
所以说,看到题目有一些基础算法的影子的时候,不要直接放弃,多尝试一下,说不定试着试着就对了呢。
题解
- 根据题目初步分析类型
n 道题目 每道题 a i a_i ai分钟,总共时常不超过t,求最小的最长空题段长。
看到最小值的最大值这类字眼要很敏感,做类似这样的题目尝试用二分的思路去解答。
- 进一步思考
既然考虑使用二分,那么我们就要看看这个题目是否符合二分的性质,最优解的右边是一定能够符合题解的(后面的空题段长比最优解段长长,段长越长,我们花费的时间也就越短,也就一定能够符合题意。),而他的左边是不符合题意的(段长越短,时间也就会越长,因为最优解一定是在完成作业的边缘,时间只要长一点点,那么就会不符合题意)。
完全符合二分的特征,可以使用二分去做这一题。
二分之后怎么去判断呢?
二分枚举的的段长(二分实现最小),那么题目就转化为了,已知段长
x
x
x,在段长不超过x的情况下(实现长度最大),求所花费最小时间T(最小时间保证长度是最大的), 当
T
≤
t
T \\leq t
T≤t 时,符合题意。
这个子题目其实是这个题目:点我查看
思路是一模一样的,就不多讲了。
代码中有一个细节需要注意:
我们给定的空格是x个,但是我们滑动窗口的大小要是x + 1个,因为遇到的两个数之间可以有x个空格,那么再加上前一个可以用的空格,那么就是x + 1个空格。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 5e4 + 7, MOD = 1e9, INF = 0x3f3f3f3f;
int q[N], f[N], a[N], n, t;
bool check(int x) {
int hh = 0, tt = 0, ans = INF;
_for(i, 1, n) {
if (hh <= tt && i - q[hh] - 1 > x) ++hh;
f[i] = f[q[hh]] + a[i];
while (hh <= tt && f[i] <= f[q[tt]]) --tt;
q[++tt] = i;
if (i >= n - x) ans = min(ans, f[i]);
}
return ans <= t;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cout.tie(0), cin.tie(0);
cin >> n >> t;
_for(i, 1, n) cin >> a[i];
int l = 0, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l <<endl;
return 0;
}
以上是关于二分+DP+单调队列优化绿色通道 LibreOJ - 10181的主要内容,如果未能解决你的问题,请参考以下文章