• 满足决策单调性的 DP 的通用做法


    在研究
    http://uoj.ac/contest/37/problem/285
    这题时发现了这个东西:
    “满足决策单调性的 DP 的通用做法”

    看一道更简单的例题:

    【NOI2009】诗人小G

    大概就是:
    (f[i]=min(f[j]+cost(j+1..i))(j<i))

    然后满足决策单调性。

    一般的决策单调性的题的那种分治是做不了这个的,因为要自己推自己。

    考虑还是从左往右dp,维护一个单调队列,队列的每个元素形如((l,r,x))表示([l,r])的最优决策点目前是(x)

    对于(i),先利用队头求出(f[i]),再考虑加入它成为新的决策点。

    从队列尾开始退,如果队尾的(l)(x)处的值都不如(l)(i)优的话,就退掉这个。

    最后剩一个区间,二分分界点即可。

    时间复杂度是:(O(n~log~n imes 计算代价时间))

    分析下和分治法的不同:分治法在计算代价时,分治法同一层可以一起扫来算(有些东西只能这么算),而不用预处理或快速计算代价函数。

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i ,x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    #define db long double
    
    int T;
    
    const int N = 1e5 + 5;
    
    int n, p; db len;
    
    char s[N][35];
    db a[N], pa[N];
    
    struct nod {
    	int l, r, x;
    } z[N];
    
    db f[N];
    int fr[N];
    
    db calc(int l, int r) {
    	db s = 1, x = abs(pa[r] - pa[l] + r - l - 1 - len);
    	for(int y = p; y; y /= 2, x = x * x)
    		if(y & 1) s = s * x;
    	return s + f[l];
    }
    
    void work() {
    	scanf("%d %Lf %d", &n, &len, &p);
    	fo(i, 1, n)	{
    		scanf("%s", s[i] + 1);
    		a[i] = strlen(s[i] + 1);
    		pa[i] = pa[i - 1] + a[i];
    	}
    	int st = 1, en = 1;
    	z[1] = (nod) {1, n, 0};
    	fo(i, 1, n) {
    		while(z[st].r < i) st ++;
    		f[i] = calc(z[st].x, i);
    		fr[i] = z[st].x;
    		
    		if(z[st].r <= i) st ++; else
    			z[st].l = i + 1;
    			
    		while(st <= en && calc(i, z[en].l) < calc(z[en].x, z[en].l)) en --;
    		
    		int as;
    		if(st > en) {
    			as = i + 1;
    		} else {
    			as = z[en].r + 1;
    		}
    		for(int l = z[en].l, r = z[en].r; l <= r; ) {
    			int m = l + r >> 1;
    			if(calc(i, m) < calc(z[en].x, m)) {
    				as = m, r = m - 1;
    			} else l = m + 1;
    		}
    		z[en].r = as - 1;
    		if(as <= n) z[++ en] = (nod) {as, n, i};
    	}
    	if(f[n] > 1e18) {
    		pp("Too hard to arrange
    ");
    		return;
    	}
    	pp("%.0Lf
    ", f[n]);
    	static int d[N][2], d0;
    	d0 = 0;
    	for(int x = n; x; x = fr[x])
    		d[++ d0][0] = fr[x] + 1, d[d0][1] = x;
    	fd(i, d0, 1) {
    		fo(j, d[i][0], d[i][1]) {
    			fo(k, 1, a[j]) pp("%c", s[j][k]);
    			if(j != d[i][1]) pp(" ");
    		}
    		hh;
    	}
    }
    
    int main() {
    	scanf("%d", &T);
    	fo(ii, 1, T) {
    		work();	
    		pp("--------------------
    ");
    	}
    }
    
  • 相关阅读:
    重构的体会——类属性优先移动
    jQuery实现无限循环滚动公告
    jquery菜单左右翻屏效果
    44种IE css bug实例测试总结
    IE6不支持position:fixed的解决方法
    DedeCMS会员排行调用代码,实现连接到会员空间
    程序员们 不要想一辈子靠技术混饭吃
    Load JSON data with jQuery, PHP and MySQL
    mysql 实现行号的方法——如何获取当前记录所在行号
    jQuery精仿手机上的翻牌效果菜单
  • 原文地址:https://www.cnblogs.com/coldchair/p/13374827.html
Copyright © 2020-2023  润新知