zoukankan      html  css  js  c++  java
  • 动态规划

    本篇博客同时作为 WC2021 Day1 的植入的搬迁出来的某篇文章。

    适用范围

    1D/1D 的动态规划中存在一个与状态有关的变量与转移有关变量的乘积项时所用的 DP 优化,可以将时间复杂度从平方阶优化到线性、

    ?

    1.把状态方程变换为平面的斜率问题

    例题 HDU 3507 Print Article

    Description

    给定序列 (c),将序列划分成若干段,每段的代价为 (sum c_i + M)(M) 是给定的常数,最小化总代价。

    Solution

    待填。

    例题 HNOI2008 玩具装箱

    Description

    给定序列 (c) 和参数 (L),将 (c) 划分为若干段,每一段的代价为 ((sum c_i + length - 1 - L) ^ 2),最小化代价总和。

    Solution

    (f_{i}) 表示将前 (i) 个划分完成的最小代价,显然有:

    [f_{i} = min_{1 le j < i}{f_j + left(i - j - 1 + sum_i - sum_j - L ight) ^ 2} ]

    其中 (sum_i)(c) 的前缀和。

    发现直接求解的复杂度是 (mathcal Oleft(n^2 ight)) 的,不够优秀。

    考虑优化。

    发现对于 (i in [1, n]),使得 (f_i) 最小时取得的 (j) 是随着 (i) 的递增单调不降的。

    因此决策满足单调性。

    设当决策点取 (j) 的时候,(f_i) 取得最优决策,则转移式的 (min) 可以去掉,即:

    [f_{i} = f_j + left(i - j - 1 + sum_i - sum_j - L ight) ]

    代换一下,设 (a_i = i - 1 + sum_i - L, b_i = sum_i + j),因此原式可以化为:

    [f_j + b_j^2 = 2 a_i b_i + f_i - a_i ^2 ]

    其中 (f_j) 在之前已经被求出,(a_i,b_j) 都是常亮,发现 这个式子就是一个自变量为 (b_i) 的方程,(2 a_i) 是斜率,(f_i - a_i^2) 是截距。

    我们要实现的就是找让这个截距最小的过程。

    用单调队列维护下凸壳,则队列内由相邻两个点构成线段所在的直线满足斜率单调递增,每次找到第一个斜率大于 (2 a_i) 的位置,即符合条件的决策点。

    由于决策满足单调性,之前的决策点可以直接弹出。

    每个决策点只进出了一次单调队列,因此复杂度为 (mathcal O left(n ight))

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int Maxn = 5e4 + 100;
    int f[Maxn], c[Maxn], sum[Maxn], a[Maxn], b[Maxn];
    inline double slope(int d1, int d2) {return (double)(f[d1] + b[d1] * b[d1] - f[d2] - b[d2] * b[d2]) / (double)(b[d1] - b[d2]);}
    int q[Maxn], h = 0, t = 0;
    int n, L;
    signed main() {
    	ios::sync_with_stdio(false);
    	cin >> n >> L;
    	for(int i = 1; i <= n; ++i) {
    		cin >> c[i];
    		sum[i] = sum[i - 1] + c[i];
    	}
    	for(int i = 1; i <= n; ++i) {
    		a[i] = i - 1 + sum[i] - L;
    		b[i] = sum[i] + i;
    	}
    	for(int i = 1; i <= n; ++i) {
    		while((h < t) && (slope(q[h], q[h + 1]) < 2 * a[i])) h++;
    		f[i] = f[q[h]] + b[q[h]] * b[q[h]] - 2 * a[i] * b[q[h]] + a[i] * a[i];
    		while((h < t) && (slope(q[t], i) <= slope(q[t], q[t - 1]))) t--;
    		q[++t] = i;
    		//cout << h << " " << f[h] << "
    ";
    	}
    	cout << f[n];
    	return 0; 
    }
    
     
    
  • 相关阅读:
    VS2005调试问题与解决方法集锦
    [转]在.net 2005中,将数据集序列化成二进制,提高远程传输效率
    DotNet 网上相关资源
    NeHE中文学习网址
    Aspx页上设置客户端的缓存时间
    动态调用Win32 Function(API)
    PB调用COM组件
    字段绑定的效率问题
    三维向量类
    用DataTable绑定TreeView的方法
  • 原文地址:https://www.cnblogs.com/zimujun/p/14355569.html
Copyright © 2011-2022 走看看