代码改变世界

利用单调队列优化动态规划

2012-10-12 19:05  kliner  阅读(490)  评论(0编辑  收藏  举报

  可用单调队列优化的动规有一大类题型,它们多半都有一个特征:可以化归为序列中定长区间的最值问题。注意这里必须是定长区间,否则应用RMQ算法。下面举一个例子:

  输入:第一行两个正整数N(N<=600000),M。接下来一行N个数。

  输出:对于每个区间[i,i-M+1],输出其中的最小值。

  思路:很显然这道题数据大到不允许利用RMQ的各种O(NlogN)的算法,想到每一次找最小值都只是将上一个区间后移一个数,即这个区间的答案很有可能可从上个区间获得,于是保持一个单调队列,区间每后移一次,就将num[i]插入队尾,若队尾的数Q[k]大于等于当前的数(num[i]),就说明Q[k]在以后就不可能是一个可行解,删去它,这样循环操作,直至Q[k]<num[i]为止。

  对于每一个区间的最小值,只需输出当前队最前面的且在num中的下标号大于i-M+1的值即可。这样复杂度就从O(NlogN)飞跃到了O(N)。

  此外,有很多问题可以化归为此问题进行解决,如求序列中长度不大于定值的最大连续子序列和等问题,并且此方法可以套用于满足上述性质的非动归题目,应用范围极广。

  以下贴出我的代码:

 1 #include <iostream>
 2 #include <fstream>
 3 using namespace std;
 4 ifstream fin("IQD.in");
 5 ofstream fout("IQD.out");
 6 int N,num[2000001],Q[2000005],M;
 7 int main()
 8 {
 9 ios::sync_with_stdio(false);
10 fin>>N>>M; //N代表有N个数,M代表区间定长为M,求出最小值;
11 int i=1;
12 for(;i<=N;i++)
13 fin>>num[i];
14 int head=1,tail=1;
15 Q[tail]=1;
16 for(i=2;i<=M;i++)
17 {
18 tail++;
19 while(num[i]<=num[Q[tail-1]]&&tail-1>=head)
20 {
21 tail--;
22 }
23 Q[tail]=i;
24 }
25 for(i=M;i<=N;i++)
26 {
27 fout<<num[Q[head]]<<" ";
28 tail++;
29 if(i-M+1>=Q[head])
30 head++;
31 while(num[i]<=num[Q[tail-1]]&&tail-1>=head)
32 {
33 tail--;
34 }
35 Q[tail]=i;
36 }
37 return 0;
38 }