zoukankan      html  css  js  c++  java
  • [luogu3198] 玩具装箱

    题面

    ​ 最近在搞dp, 发现自己的dp还是太弱了, 做的题比较少, 也有一些东西没学, 这道题算是我独立做的第一道斜率优化dp, 写篇题解纪念一下吧.

    ​ dp式比较简单, 就是一个线性的dp, 设(f[i])为将1 ~ i分为若干个集合所花费的最小值, (c[i])为1 ~ i的(c)值的前缀和, 对于满足(j)小于(i)(j), 则有:

    [f[i] = min(f[j] + (c[i] - c[j] + i - j - 1 - l)^2) ag{1} ]

    (a[i] = c[i] + i), (b[i] = c[i] + i + 1 + l), 则可将上式化简为下式:

    [egin{aligned} f[i] = & min(f[j] + (a[i] - b[j])^2)\ =&min(f[j] + a[i] ^ 2 + b[j] ^ 2 - 2 * a[i] * b[j]) end{aligned} ]

    假设对于某个(k)((k) >(j)), 选(k)比选(j)更优, 则有下式:

    [egin{aligned} f[k] + a[i] ^ 2 + b[k] ^ 2 - 2 * a[i] * b[k] &< f[j] + a[i] ^ 2 + b[j] ^ 2 - 2 * a[i] * b[j]\ (f[k] + b[k] ^ 2) - (f[j] + b[j] ^ 2) &< 2 * a[i] * (b[k] - b[j])\ frac{(f[k] + b[k] ^ 2) - (f[j] + b[j] ^ 2)}{2 * (b[k] - b[j])} &< a[i] end{aligned} ]

    对于上述几个不等式, 由于(c[i])(i)单调递增, 所以(a[i])也是单调递增的, 那么对于平面上的任意三个决策点(j_1), (j_2), (j_3), 若(j_2)(i)最优, 则((b[j_1], f[j_1] + b[j _ 1] ^ 2)), ((b[j_2], f[j_2] + b[j _ 2]^2)), ((b[j_3], f[j _ 3] + b[j _ 3] ^ 2))三点应该满足下列两个等式:

    [egin{aligned} frac{(f[j_2] + b[j_2] ^ 2) - (f[j _ 1] + b[j _ 1] ^ 2)}{2 * (b[j_2] - b[j _ 1])} &< a[i]\ frac{(f[j_3] + b[j_3] ^ 2) - (f[j _ 2] + b[j _ 2] ^ 2)}{2 * (b[j_3] - b[j _ 2])} &geq a[i]\ end{aligned} ]

    所以有:

    [frac{(f[j_3] + b[j_3] ^ 2) - (f[j _ 2] + b[j _ 2] ^ 2)}{2 * (b[j_3] - b[j _ 2])} >frac{(f[j_2] + b[j_2] ^ 2) - (f[j _ 1] + b[j _ 1] ^ 2)}{2 * (b[j_2] - b[j _ 1])} ]

    也就是斜率单调上升, 故维护一个下凸包即可, 注意单调队列初始化时要先加入一个0.

    代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define N 50005
    #define int long long
    using namespace std;
    
    int n, L, sum[N], a[N], b[N], f[N], q[N], l = 1, r; 
    
    inline int read()
    {
    	int x = 0, w = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    	return x * w;
    }
    
    bool F_check(int x, int y, int z) { return f[y] - f[x] + b[y] * b[y] - b[x] * b[x] < 2 * z * (b[y] - b[x]); }
    
    bool S_check(int x, int y, int z)
    {
    	return (f[y] - f[x] + b[y] * b[y] - b[x] * b[x]) * (b[z] - b[y]) > (f[z] - f[y] + b[z] * b[z] - b[y] * b[y]) * (b[y] - b[x]); 
    }
    
    signed main()
    {
    	n = read(); L = read(); 
    	for(int i = 1; i <= n; i++)
    	{
    		sum[i] = read(); sum[i] += sum[i - 1];
    		a[i] = sum[i] + i; b[i] = sum[i] + i + L + 1; 
    	}
    	memset(f, 0x3f, sizeof(f));
    	b[0] = L + 1; f[0] = 0;
    	q[++r] = 0; 
    	for(int i = 1; i <= n; i++)
    	{
    		while(l < r && F_check(q[l], q[l + 1], a[i])) l++;
    		f[i] = f[q[l]] + a[i] * a[i] + b[q[l]] * b[q[l]] - 2 * a[i] * b[q[l]];
    		while(l < r && S_check(q[r - 1], q[r], i)) --r;
    		q[++r] = i; 
    	}
    	printf("%lld
    ", f[n]); 
    	return 0;
    }
    \有一些数组的名字改动了一点点, 世上不是缺少不一样的东西, 而是缺少发现不一样的东西的眼睛
    

    你觉得我会告诉你我在写题目之前就把题解写完了吗???

  • 相关阅读:
    sed 拓展 awk 拓展
    9.6/9.7 awk
    9.4/9.5 sed
    正则介绍_grep上& grep中 & grep下
    shell特殊符号cut命令& sort_wc_uniq命令 & tee_tr_split命令 & shell特殊符号
    管道符和作业控制 & shell变量& 环境变量配置文件
    shell介绍 & 命令历史 & 命令补全和别名& 通配符& 输入输出重定向
    yum更换国内源 & yum下载rpm包 & 源码包安装
    网络编程入门(下)
    网络编程入门(上)
  • 原文地址:https://www.cnblogs.com/ztlztl/p/10616538.html
Copyright © 2011-2022 走看看