Codeforces 487b Strip, dp + RMQ(经典)
来源:互联网 发布:科沃尔数据 编辑:程序博客网 时间:2024/05/18 03:17
- 题意:有一个长度为n的数列,问是否能把这个数列切成连续的几段,使得 1.每一段长度大于等于l;2.每一段中的最大值和最小值之差小于等于s。如果能输出能切成的最小的段数,不能输出-1。
思路:非常经典的dp题目,假设dp[i]表示
a1...ai 这个序列能切成的最小段数,如果不能dp[i] = INF。现在问题是1.如何找到状态转移方程。考虑如果l = 3, s = 1,如果
ai=5 ,ai−1=3 , 很明显a1...ai 无论如何都不能切成满足条件的小段,所以dp[i]=INF。再考虑如果对于ai ,aj−k...ai ,aj...ai ,都是满足条件的一段,很容易想到dp[i] = min(dp[j-k]…dp[j]) + 1。 2. 有了状态转移方程,现在问题便是如何维护当前区间的最大值最小值以及与ai 配对,满足条件的dp[j-k]…dp[j]的最小值,即RMQ问题。这里用ST表,线段树都可以,但是因为这里可以采用尺取法(二指针),区间是连续移动的(没有跳跃),所以还可以使用multiset 和 单调队列 来维护。因为这里n 只有105 所以使用multiset的复杂度是nlogn完全没有问题,使用单调队列的话是O(n),但是如果单调队列用标准库的deque实现的话,常数通常会比较大,对于数据量比较大的题目还是推荐自己手写。代码:
/************************************************************************* *Multiset 版本 ************************************************************************/#include <bits/stdc++.h>using namespace std;const int maxn = 100000 + 5;const int INF = 0x3f3f3f3f;int A[maxn];int dp[maxn];int main(){ int n, s, l; scanf("%d %d %d", &n, &s, &l); for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); } multiset<int> pre, cur; int i = 1, j = 1; for(i = 1; i <= n; i++) { cur.insert(A[i]); while(*cur.rbegin() - *cur.begin() > s) { cur.erase(cur.find(A[j])); if(pre.find(dp[j - 1]) != pre.end()) { pre.erase(pre.find(dp[j - 1])); } j++; } if(i - j + 1 >= l) pre.insert(dp[i - l]); if(pre.empty()) { dp[i] = INF; } else { dp[i] = *pre.begin() + 1; } } int ans = dp[n]; if(ans >= INF) ans = -1; cout << ans << endl; return 0;}/************************************************************************* * 单调队列版本 ************************************************************************/#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 100000 + 5;int A[maxn];int dp[maxn];deque<int> min_que, max_que, pre_que;void add(int x) { while(!min_que.empty() && x < min_que.back()) min_que.pop_back(); min_que.push_back(x); while(!max_que.empty() && x > max_que.back()) max_que.pop_back(); max_que.push_back(x);}void del(int x) { if(!min_que.empty() && x == min_que.front()) min_que.pop_front(); if(!max_que.empty() && x == max_que.front()) max_que.pop_front();}void pre_add(int x) { while(!pre_que.empty() && x < pre_que.back()) pre_que.pop_back(); pre_que.push_back(x);}void pre_del(int x) { if(!pre_que.empty() && pre_que.front() == x) pre_que.pop_front();}int main(){ int n, s, l; scanf("%d %d %d", &n, &s, &l); for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); } int i = 1, j = 1; for(i = 1; i <= n; i++) { add(A[i]); while(max_que.front() - min_que.front() > s) { del(A[j]); pre_del(dp[j-1]); j++; } if(i - j + 1 >= l) pre_add(dp[i-l]); if(pre_que.empty()) dp[i] = INF; else dp[i] = pre_que.front() + 1; } int ans = dp[n]; if(ans >= INF) ans = -1; cout << ans << endl; return 0;}
0 0
- Codeforces 487b Strip, dp + RMQ(经典)
- Codeforces 487B Strip(RMQ)
- codeforces 487B Strip dp
- 【DP】 codeforces 487B Strip
- codeforces 487B B. Strip(rmq+线段树+二分)
- CodeForces 487 B.Strip(dp+尺取+set)
- Codeforces 488D Strip【dp+RMQ--------ST】
- Codeforces 488D Strip【dp+RMQ--------ST】
- CodeForces 487B Strip
- codeforces 487 B. Strip
- CodeForces 487B Strip
- Codeforces 487B. Strip DP+线段树+二分
- 【Monotonic-queue】【dp】【Segment-tree】【STL】Codeforces 487B - Strip
- Codeforces 487B. Strip(求区间最值+线段树上的dp)
- Codeforces Round #278 (Div. 1) B. Strip(Dp+multiset维护)
- CodeForces 487B RMQ贪心
- Codeforces Round #193 (Div. 2) B. Maximum Absurdity (DP、RMQ两种解法)
- Codeforces Round #278 (Div. 1) B. Strip
- Hbase总结:Hbase中的Coprocessor
- Java父类、子类、静态成员和普通成员初始化的顺序
- Oracle建立表空间和用户
- Gate架构
- git 笔记
- Codeforces 487b Strip, dp + RMQ(经典)
- linux 网线插拔检测
- <PY>利用7z暴力破解压缩文件密码
- Gson:比较两个JSON字符串是否完全相等
- 开发者必知的KPI概念和用法
- 在一个文件中有 10G 个整数,乱序排列,要求找出中位数(内存限制为2G)
- 在Windows Server2003中运行3D游戏
- 强制去掉警告
- 史上最详细的Android Studio系列教程二--基本设置与运行