• 洛谷P2300 合并神犇


    一道单调队列的模板题。

    看到数据范围不难想到一维(DP)数组(f_i)表示将(1)(i)的数合并后满足单调不降的最小合并次数。

    考虑(f_i)是由什么转移得到的。发现(f_i)并不只是和(f_{i-1})有关,实际上,由于合并的性质,(f_i)可以由前面任意一个(f_j)得到,但前提是由(i)(j)合并得到的数要不小于为了得到(f_j)所合并后的序列的最后一个数。

    我们设(s_i)表示(p_i)的前缀和,(last_i)表示得到(f_j)所合并后的序列的最后一个数。那么转移方程就是(f_i=min{f_j+i-j-1}(s_i-s_jgeq last_j))。这样暴力转移是(O(n^2))的。

    考虑优化转移。对于(k=j+1)来说,(f_kleq f_j+1)。因为如果(f_k>f_j+1)的话,我们只需要在(f_j)的基础上将新加入的数与前面合并即可得到(f_k=f_j+1)。而(j)每增加一,(f_i+i-j-1)一定会减少(1)。也就是说我们要找的(j)其实是满足(s_i-s_jgeq last_j)的最大的(j)

    对式子(s_i-s_jgeq last_j)进行变形得到(s_j+last_jleq s_i)。发现(s_i)是单调上升的,也就是对于一个固定的(j)来说,(s_j+last_j)的值越小,就越有可能在更多的转移中被选择。

    对于(j<k),如果(s_k+last_kleq s_j+last_j),也就是(k)在比(j)后入队的同时(即比(j)优),还比(j)更容易在更多的选择中被转移,那么(j)也就没用了,因为无论如何能选择(j)的情况一定可以选择(k)

    	int l = 1, r = 0;
    	for(int i = 1; i <= n; i ++)
    	{
    		while(l <= r && s[q[l]] + last[q[l]] <= s[i]) l ++;
    		f[i] = f[q[l - 1]] + i - q[l - 1] - 1;
    		last[i] = s[i] - s[q[l - 1]];
    		while(l <= r && s[i] + last[i] <= s[q[r]] + last[q[r]]) r --;
    		q[++r] = i;
    	}
    
  • 相关阅读:
    ES自身支持容灾异地容灾么?生产环境如何实施?
    Redis集群详解
    原生js实现jquery的ajax
    用原生js实现jquery的一些方法
    原生javascript的一些常用方法
    原生javascript
    理解和熟练运用call和apply
    做项目过程中的css reset
    深入理解javascript编程中的同步和异步
    history.back(-1)和history.go(-1)的区别
  • 原文地址:https://www.cnblogs.com/lcezych/p/13490584.html
Copyright © 2020-2023  润新知