利用单调队列优化动态规划
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 }