题目大意:
1.给出长度为n的数组,要求每m个连续的元素之间必须选一个值作为代价,求该数组的最小代价。
题解思路:
1.显然是线性dp,dp【i】表示选择第 i 个元素时的最小总代价。很明显状态转移方程为 dp[i] = min(dp[j]) + a[i]。(i - m <= j <= i - 1)。但是在求min(dp[j])的时候,我们需要遍历一遍长度为m大小的区间,m极限与n同大。时间负责度0(N^2)。显然会超时。
2.我们需要用单调队列来维护长度为m大小区间内的最小值,队头即为最小值,直接O(1)得到min(dp[j]),就可以避免超时。
代码如下:

1 #include<stdio.h>
2 #include<deque>
3 #include<algorithm>
4 const int MAXN = 2e5 + 100;
5 using namespace std;
6 const int inf = 0x3f3f3f3f;
7
8 int n, m, a[MAXN];
9 int dp[MAXN]; //表示第 i 个烽火台放置烽火时的最小总代价
10 deque<int> Q;
11
12 int main()
13 {
14 scanf("%d%d",&n, &m);
15 for(int i = 1; i <= n; i ++)
16 scanf("%d", &a[i]);
17 for(int i = 1; i <= m; i ++) //dp以及单调队列初始化
18 {
19 dp[i] = a[i];
20 while(!Q.empty())
21 {
22 if(dp[i] < dp[Q.back()])//保证队头保存的是dp值最小的下标 队头到队尾单调递增
23 Q.pop_back();
24 else
25 break;
26 }
27 Q.push_back(i);
28 }
29 for(int i = m + 1; i <= n; i ++)
30 {
31 while(!Q.empty())
32 {
33 if(i - m > Q.front()) //保证队头位于枚举范围内,即是在 [i - m, i - 1]范围内
34 Q.pop_front();
35 else
36 break;
37 }
38 dp[i] = dp[Q.front()] + a[i];
39 while(!Q.empty())
40 {
41 if(dp[i] < dp[Q.back()])
42 Q.pop_back();
43 else
44 break;
45 }
46 Q.push_back(i);
47 }
48 int ans = inf;
49 for(int i = n; i > n - m; i --)
50 ans = min(ans, dp[i]);
51 printf("%d\n", ans);
52 return 0;
53 }
54 /*
55 5 3
56 1 2 5 6 2
57
58 4
59 */
