• BZOJ1010玩具裝箱Toy


    @[斜率優化]

    Description

    P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
    缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中. P教授有编号为(1 .. N)(N)件玩具,第(i)件玩具经过压缩后变成一维长度为(C_i).为了方便整理, P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
    器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 $$x = j - i + sum_{k = i}^{j} C_i$$ 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为(x), 其制作费用为((X-L)^2). 其中(L)是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过(L). 但他希望费用最小.

    Input

    第一行输入两个整数N,L.接下来N行输入(C_i). (1 <= N <= 50000, 1 <= L, C_i <= 10^7)

    Output

    输出最小费用

    Sample Input

    5 4
    3
    4
    2
    1
    4
    

    Sample Output

    1
    

    Solution

    典型的斜率優化DP
    (f[i])記錄標號為從(1)(i)的玩具全部壓縮好所需要的最小費用, 則可以得到DP递推式$$f[i] = min_{j = 0}^{i - 1}(f[j] + (sum[i] - sum[j] + i - j - 1 - L)^2)$$其中(sum[i])記錄編號從(1)(i)的玩具的長度總和.
    假設在(i > j > k)中, 對於(i)(j)(k)優, 則$$f[j] + (sum[i] - sum[j] + i - j - 1 - L)^2 < f[k] + (sum[i] - sum[k] + i - k - 1 - L)^2$$
    化簡得到

    [frac{(f[j] + b[j]^2) - (f[k] + b[k]^2)}{b[j] - b[k]} < 2 * a[i] ]

    其中$$a[i] = sum[i] + 1 - L$$$$b[i] = sum[i] + i$$

    *Hint: 化簡有一定技巧. 一般來說, 化簡得到的結果要把含(i)的項移至等號右邊, 不含(i)的項移至左邊, 以方便後續運算.

    可以將這個除法式子理解為所謂的斜率, 記為(slope(j, k))

    然後用隊列來維護DP. 具體過程如下:

    1. 對於隊頭的兩個元素, 假如有(slope(queue[head + 1], queue[head]) > 2 * a[i])則說明隊列中第二個元素比第一個優, 隊頭出隊.
    2. 此時可以確保隊頭元素是最優解, 用隊頭元素計算出(f[i])的數值
    3. 在有了(f[i])的值的情況下, 就可以在隊尾進行維護了. 對於隊尾的兩個元素記為(x, y(x > y)), 假如有(slope(x, y) > slope(i, x))則說明(x)是無用的, 可以出隊. 具體證明如下: (1). 假如(slope(x, y) > slope(i, x) > 2 * a[i]), 則雖然有(x)(i)優, 但又有(y)(x)優, 因此(x)可出隊; (2). 假如(slope(x, y) > slope(i, x) < 2 * a[i]), 則有(i)(x)優, (x)可出隊.
    4. (i)加入隊尾

    維護過程結束. 注意每一步的順序, 都是有先後性的, 不要搞反.
    然後再說道一個點, 這一題一定要開(long long)
    感覺現階段推公式的能力還要加強, 這一坨東西我推錯了好多次QAQ
    附上代碼

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    inline long long read()
    {
    	long long x = 0, flag = 1;
    	char c;
    	while(! isdigit(c = getchar()))
    		if(c == '-')
    			flag *= - 1;
    	while(isdigit(c))
    		x = x * 10 + c - '0', c = getchar();
    	return x * flag;
    }
    const long long N = 1 << 16;
    long long c[N];
    long long sum[N];
    long long a[N], b[N];
    long long queue[N];
    long long f[N];
    inline long long sqr(long long x)
    {
    	return x * x;
    }
    inline long double slope(long long x, long long y)
    {
    	return (long double)((f[x] + sqr(b[x])) - (f[y] + sqr(b[y]))) / (long double)(b[x] - b[y]);
    }
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("BZOJ1010.in", "r", stdin);
    	freopen("BZOJ1010.out", "w", stdout);
    	#endif
    	long long n = read(), l = read();
    	sum[0] = 0;
    	a[0] = - 1 - l;
    	b[0] = 0;
    	for(long long i = 1; i <= n; i ++)
    	{
    		c[i] = read();
    		sum[i] = c[i] + sum[i - 1];
    		a[i] = sum[i] + i - 1 - l;
    		b[i] = sum[i] + i;
    	}
    	long long head = 0, tail = 1;
    	queue[head] = 0; 
     	memset(f, 0, sizeof(f));
    	for(long long i = 1; i <= n; i ++)
    	{
    		while(head + 1 < tail && slope(queue[head + 1], queue[head]) < 2 * (float)a[i])
    			head ++;
    		f[i] = f[queue[head]] + sqr(a[i] - b[queue[head]]);
    		while(head + 1 < tail && slope(queue[tail - 1], queue[tail - 2]) > slope(i, queue[tail - 1]))
    			tail --;
    		queue[tail ++] = i;
    	}
    	printf("%lld
    ", f[n]);
    }
    

    順便, 這裡想借這一題, 寫一點關於斜率優化的深層次理解, 主要圍繞隊列的維護方面. 首先是入隊, 為什麼要通過這種方式確定隊尾元素的保留還是彈出? 為什麼在(slope(i, queue[tail - 1]) > 2 * a[i])的情況下仍然要將(i)入隊? 主要是出於對後效性的考慮. 對於(2 * a[i])不難看出, 它是隨著(i)的增大而遞增的. 因此, 對於一個(i), 雖然在當前不一定是最優的, 但在之後可能成為最優解, 因此要入隊. 因此在隊尾出隊的原則, 應該是與當前的(a[i])無關的. 至於在隊頭出隊的原則, 也是在考慮到(2 * a[i])的單調性以及確保無後效性的情況下才出隊的.
    另外一點就是關於判斷一道題是否可以進行斜率优化. 這主要取決於等式右邊的斜率是否滿足單調性. 雖然說從隊尾出隊的元素與斜率無關, 但是隊頭出隊的元素是需要依賴於斜率的單調性的. 因此斜率是否滿足單調性可以作為一題是否可以採用 常規 的斜率优化的判斷依據.(假如不滿足, 還可以採用特殊的方法優化DP)

  • 相关阅读:
    【问题】SUSE已经安装了libsodium,安装zeromq时出现下面的错误?
    将博客搬至CSDN
    iOS开发之self.abc = nil与[_abc release]的区别
    Swift编程语言入门教程
    如何使用NSData处理数据
    集合之間的轉換
    數組排序
    集合类的用法总结(NSArray、NSDictionary、NSSet)
    NSString的各种用法总结(创建、截取、判断比较、转化数据类型、拼接、替换、添加、追加、读取、写入、删去、改变)
    instancetype和id的区别
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6483073.html
Copyright © 2020-2023  润新知