Codeforces 487b Strip, dp + RMQ(经典)
2015-09-06 10:21
405 查看
题意:有一个长度为n的数列,问是否能把这个数列切成连续的几段,使得 1.每一段长度大于等于l;2.每一段中的最大值和最小值之差小于等于s。如果能输出能切成的最小的段数,不能输出-1。
思路:非常经典的dp题目,假设dp[i]表示a1...aia_1...a_i这个序列能切成的最小段数,如果不能dp[i] = INF。现在问题是
1.如何找到状态转移方程。考虑如果l = 3, s = 1,如果ai=5a_i = 5, ai−1=3a_{i-1} = 3, 很明显a1...aia_1...a_i 无论如何都不能切成满足条件的小段,所以dp[i]=INF。再考虑如果对于aia_i, aj−k...aia_{j-k}...a_i, aj...aia_j...a_i,都是满足条件的一段,很容易想到dp[i] = min(dp[j-k]…dp[j]) + 1。 2. 有了状态转移方程,现在问题便是如何维护当前区间的最大值最小值以及与aia_i配对,满足条件的dp[j-k]…dp[j]的最小值,即RMQ问题。这里用ST表,线段树都可以,但是因为这里可以采用尺取法(二指针),区间是连续移动的(没有跳跃),所以还可以使用multiset 和 单调队列 来维护。因为这里n 只有10510^5 所以使用multiset的复杂度是nlogn完全没有问题,使用单调队列的话是O(n),但是如果单调队列用标准库的deque实现的话,常数通常会比较大,对于数据量比较大的题目还是推荐自己手写。
代码:
思路:非常经典的dp题目,假设dp[i]表示a1...aia_1...a_i这个序列能切成的最小段数,如果不能dp[i] = INF。现在问题是
1.如何找到状态转移方程。考虑如果l = 3, s = 1,如果ai=5a_i = 5, ai−1=3a_{i-1} = 3, 很明显a1...aia_1...a_i 无论如何都不能切成满足条件的小段,所以dp[i]=INF。再考虑如果对于aia_i, aj−k...aia_{j-k}...a_i, aj...aia_j...a_i,都是满足条件的一段,很容易想到dp[i] = min(dp[j-k]…dp[j]) + 1。 2. 有了状态转移方程,现在问题便是如何维护当前区间的最大值最小值以及与aia_i配对,满足条件的dp[j-k]…dp[j]的最小值,即RMQ问题。这里用ST表,线段树都可以,但是因为这里可以采用尺取法(二指针),区间是连续移动的(没有跳跃),所以还可以使用multiset 和 单调队列 来维护。因为这里n 只有10510^5 所以使用multiset的复杂度是nlogn完全没有问题,使用单调队列的话是O(n),但是如果单调队列用标准库的deque实现的话,常数通常会比较大,对于数据量比较大的题目还是推荐自己手写。
代码:
[code]/************************************************************************* *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 ; 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 ; if(ans >= INF) ans = -1; cout << ans << endl; return 0; }
相关文章推荐
- <PY>利用7z暴力破解压缩文件密码
- 开发者必知的KPI概念和用法
- 第二次作业
- poj2774 后缀数组
- COMBINATORIAL TESTING
- LeetCode_Remove Element
- Oracle建立表空间和用户
- Java父类、子类、静态成员和普通成员初始化的顺序
- Linux_shell——第2章 命令之乐
- android MVP框架
- android MVP框架
- C# 实现磁性窗体
- Hbase总结:Hbase中的Coprocessor
- 取消cell点击效果
- Gate架构
- C# 实现磁性窗体
- android MVP框架
- mysql utf-general-ci 修改成utf8mb4 方案
- RMAN catalog备份恢复方案
- iOS开发小白学习体验-5