烽火传递【单调队列优化dp】

题目大意:

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 */ 
View Code

 

posted @ 2019-11-25 17:34  缘未到  阅读(190)  评论(0编辑  收藏  举报