我是靠谱客的博主 殷勤斑马,这篇文章主要介绍Codeforces 487b Strip, dp + RMQ(经典),现在分享给大家,希望可以做个参考。

  • 题意:有一个长度为n的数列,问是否能把这个数列切成连续的几段,使得 1.每一段长度大于等于l;2.每一段中的最大值和最小值之差小于等于s。如果能输出能切成的最小的段数,不能输出-1。
  • 思路:非常经典的dp题目,假设dp[i]表示 a1...ai 这个序列能切成的最小段数,如果不能dp[i] = INF。现在问题是

    1.如何找到状态转移方程。考虑如果l = 3, s = 1,如果 ai=5 , ai1=3 , 很明显 a1...ai 无论如何都不能切成满足条件的小段,所以dp[i]=INF。再考虑如果对于 ai , ajk...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实现的话,常数通常会比较大,对于数据量比较大的题目还是推荐自己手写。

  • 代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/************************************************************************* *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; }

最后

以上就是殷勤斑马最近收集整理的关于Codeforces 487b Strip, dp + RMQ(经典)的全部内容,更多相关Codeforces内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(81)

评论列表共有 0 条评论

立即
投稿
返回
顶部