Loading docs/dp/optimization.md +108 −75 Original line number Diff line number Diff line Loading @@ -2,6 +2,114 @@ By [hsfzLZH1](https://github.com/hsfzLZH1) 本章主要讲解动态规划的几种基础 ** 优化 ** 方法。 ## 二进制优化解多重背包 ### 例题 [经典问题-多重背包](https://oi-wiki.org/dp/backpack/#_3) 题目大意:有 $n$ 种物品,每种物品有 $a_i$ 件,购买一件这种物品的费用为 $c_i$,价值为 $v_i$。有一个容量为 $t$ 的背包,现在让你找到最优的一种方案,使得装入背包的物品的总价值最大。 考虑常规的动规方式,定义 $f_{i,j}$ 为当前考虑到第 $i$ 个物品,背包容量为 $j$ 所能获得的最大价值。 状态转移方程为, $f_{i,j}=\max\{f_{i-1,j},f_{i-1,j-c_i}+v_i\}$。 对于 ** 每件 ** 物品,都要这样循环一次,时间复杂度为 $t\times \sum_{i=1}^n a_i$,某些时候可能不可接受,需要优化。 考虑这样一种情况,如果我们有 $17$ 个硬币,要去买 $1$ 到 $17$ 元钱的物品,只需将这些硬币打包成 $1,2,4,8$ 和 $2$ 这样的几包。前面的 $4$ 包能保证覆盖 $1$ 到 $15$ 所有的情况,最后一包在之前的基础上再加上一个值,能保证实现支付的时候取整包,肯定能保证支付。这就是二进制优化的原理和基本思想。 用上述的方法,就可以把 $k$ 件相同的物品看作是 $O(log_2 k)$ 件物品了。优化后 ## 代码实现 ```cpp for(int i=1;i<=n;i++) { scanf("%d",a+i); tot+=c[i]*a[i]; for(int j=1;j<=a[i];j*=2)if(a[i]>=j)a[i]-=j,v[++cur]=c[i]*j; if(a[i])v[++cur]=c[i]*a[i]; } for(int i=1;i<=cur;i++)for(int j=m;j>=v[i];j--)if(f[j-v[i]])f[j]=true; ``` ## 几道练习题 [HDU 2844 Coins](http://acm.hdu.edu.cn/showproblem.php?pid=2844) ## 单调队列&单调栈优化 学习本节前,请务必先学习 [单调队列](https://oi-wiki.org/ds/monotonous-queue/)。 ### 例题 [CF372C Watching Fireworks is Fun](http://codeforces.com/problemset/problem/372/C) 题目大意:城镇中有 $n$ 个位置,有 $m$ 个烟花要放。第 $i$ 个烟花放出的时间记为 $t_i$,放出的位置记为 $a_i$。如果烟花放出的时候,你处在位置 $x$,那么你将收获 $b_i-|a_i-x|$ 点快乐值。 初始你可在任意位置,你每个单位时间可以移动不大于 $d$ 个单位距离。现在你需要最大化你能获得的快乐值。 设 $f_{i,j}$ 表示在放第 $i$ 个烟花时,你的位置在 $j$ 所能获得的最大快乐值。 写出 ** 状态转移方程 ** :$f_{i,j}=\max\{f_{i-1,k}+b_i-|a_i-j|\}$ 这里的 $k$ 是有范围的,$j-(t_{i+1}-t_i)\times d\le k\le j+(t_{i+1}-t_i)\times d$。 我们尝试将状态转移方程进行变形: 由于 $\max$ 里出现了一个确定的常量 $b_i$,我们可以将它提到外面去。 $f_{i,j}=\max\{f_{i-1,k}+b_i+|a_i-j|\}=\max\{f_{i-1,k}+|a_i-j|\}+b_i$ 如果确定了 $i$ 和 $j$ 的值,那么 $|a_i-j|$ 的值也是确定的,也可以将这一部分提到外面去。 最后,式子变成了这个样子:$f_{i,j}=\max\{f_{i-1,k}+|a_i-j|\}+b_i=\max\{f_{i-1,k}\}+|a_i-j|+b_i$ 看到这一熟悉的形式,我们想到了什么?** 单调队列优化 **。由于最终式子中的 $\max$ 只和上一状态中连续的一段的最大值有关,所以我们在计算一个新的 $i$ 的状态值时候只需将原来的 $f_{i-1}$ 构造成一个单调队列,并维护单调队列,使得其能在均摊 $O(1)$ 的时间复杂度内计算出 $\max\{f_{i-1,k}\}$ 的值,从而根据公式计算出 $f_{i,j}$ 的值。 总的时间复杂度为 $O(n\times m)$。 讲完了,让我们归纳一下单调队列优化动态规划问题的基本形态:当前状态的所有值可以从上一个状态的某个连续的段的值得到,要对这个连续的段进行 RMQ 操作,相邻状态的段的左右区间满足非降的关系。 ### 几道练习题: [luogu P1886 滑动窗口](https://www.luogu.org/problemnew/show/P1886) [luogu P2254 [NOI2005] 瑰丽华尔兹](https://www.luogu.org/problemnew/show/P2254) [luogu P2569 [SCOI2010] 股票交易](https://www.luogu.org/problemnew/show/P2569) ## 斜率优化 ### 例题 [luogu P3195 [HNOI2008]玩具装箱TOY](https://www.luogu.org/problemnew/show/P3195) 令 $f_i$ 表示前 $i$ 个物品,随意分组装在任意多个容器里所能得到的最小费用。 写出 ** 状态转移方程 ** :$f_i=max\{f_j+(pre_i-pre_i+i-j-1-L)^2\}$ ,其中 $pre_i$ 表示前 $i$ 个数的前缀和。 换元试图简化状态转移方程式: 令 $s_i=pre_i+i,L'=L+1$,则 $f_i=f_j+(s_i-s_j-L')^2$,展开,移项得 $f_i=f_j+(s_i-s_j-L')^2$ $f_i+2\times s_i\times (s_j+L')=f_j+s_i^2+(s_j+L')^2$ 我们观察到,式子的右端的所有项都只和 $i$ 有关或只和 $j$ 有关,式子左端的第一项是我们要求的目标值,式子左端的其余项都同时和 $i$ 和 $j$ 有关。我们将这个式子看作一条直线的函数解析式,形如 $b+k\times x=y$ ,和上式一一对应。我们发现如果我们要最小化 $f_i$ ,也就是说要最小化这个直线的截距,而对于每个确定的 $i$,这个直线的斜率 $s_i$ 都是确定的。  如图,我们将这个斜率固定的直线从下往上平移,直到有一个点在这条直线上,然后将新的点加入点集,这样肯定能保证所有的直线的斜率都是单调递升的(因为如果新的直线斜率小于斜率最大的直线,那么其一定不成被选择成为新的决策),所以我们相当于维护了一个下凸包。(如果求的是 $\max$ 那么就要维护一个 ** 上凸包 ** 。这种东西要具体情况具体分析,如果直线的斜率不满足单调性,那就要维护整个凸包/二分等奇技淫巧。) 可以用单调队列维护下凸包。 ### 几道练习题: [luogu P4072 [SDOI2016] 征途](https://www.luogu.org/problemnew/show/P4072) [luogu P2120 [ZJOI2007] 仓库建设](https://www.luogu.org/problemnew/show/P2120) [luogu P3628 [APIO2010] 特别行动队](https://www.luogu.org/problemnew/show/P3628) [bzoj 4709 [Jsoi2011] 柠檬](https://www.lydsy.com/JudgeOnline/problem.php?id=4709) [CF311B Cats Transport](http://codeforces.com/problemset/problem/311/B) [luogu P4027 [NOI2007] 货币兑换](https://www.luogu.org/problemnew/show/P4027) ## 四边形不等式优化 ### 例题 [luogu P1880 [NOI1995]石子合并](https://www.luogu.org/problemnew/show/P1880) Loading Loading @@ -117,78 +225,3 @@ for(int i=n;i>=1;i--) ### 参考资料 [NOIAu 的CSDN 博客](https://blog.csdn.net/noiau/article/details/72514812) ## 单调队列&单调栈优化 学习本节前,请务必先学习 [单调队列](https://oi-wiki.org/ds/monotonous-queue/)。 ### 例题 [CF372C Watching Fireworks is Fun](http://codeforces.com/problemset/problem/372/C) 题目大意:城镇中有 $n$ 个位置,有 $m$ 个烟花要放。第 $i$ 个烟花放出的时间记为 $t_i$,放出的位置记为 $a_i$。如果烟花放出的时候,你处在位置 $x$,那么你将收获 $b_i-|a_i-x|$ 点快乐值。 初始你可在任意位置,你每个单位时间可以移动不大于 $d$ 个单位距离。现在你需要最大化你能获得的快乐值。 设 $f_{i,j}$ 表示在放第 $i$ 个烟花时,你的位置在 $j$ 所能获得的最大快乐值。 写出 ** 状态转移方程 ** :$f_{i,j}=\max\{f_{i-1,k}+b_i-|a_i-j|\}$ 这里的 $k$ 是有范围的,$j-(t_{i+1}-t_i)\times d\le k\le j+(t_{i+1}-t_i)\times d$。 我们尝试将状态转移方程进行变形: 由于 $\max$ 里出现了一个确定的常量 $b_i$,我们可以将它提到外面去。 $f_{i,j}=\max\{f_{i-1,k}+b_i+|a_i-j|\}=\max\{f_{i-1,k}+|a_i-j|\}+b_i$ 如果确定了 $i$ 和 $j$ 的值,那么 $|a_i-j|$ 的值也是确定的,也可以将这一部分提到外面去。 最后,式子变成了这个样子:$f_{i,j}=\max\{f_{i-1,k}+|a_i-j|\}+b_i=\max\{f_{i-1,k}\}+|a_i-j|+b_i$ 看到这一熟悉的形式,我们想到了什么?** 单调队列优化 **。由于最终式子中的 $\max$ 只和上一状态中连续的一段的最大值有关,所以我们在计算一个新的 $i$ 的状态值时候只需将原来的 $f_{i-1}$ 构造成一个单调队列,并维护单调队列,使得其能在均摊 $O(1)$ 的时间复杂度内计算出 $\max\{f_{i-1,k}\}$ 的值,从而根据公式计算出 $f_{i,j}$ 的值。 总的时间复杂度为 $O(n\times m)$。 讲完了,让我们归纳一下单调队列优化动态规划问题的基本形态:当前状态的所有值可以从上一个状态的某个连续的段的值得到,要对这个连续的段进行 RMQ 操作,相邻状态的段的左右区间满足非降的关系。 ### 几道练习题: [luogu P1886 滑动窗口](https://www.luogu.org/problemnew/show/P1886) [luogu P2254 [NOI2005] 瑰丽华尔兹](https://www.luogu.org/problemnew/show/P2254) [luogu P2569 [SCOI2010] 股票交易](https://www.luogu.org/problemnew/show/P2569) ## 斜率优化 ### 例题 [luogu P3195 [HNOI2008]玩具装箱TOY](https://www.luogu.org/problemnew/show/P3195) 令 $f_i$ 表示前 $i$ 个物品,随意分组装在任意多个容器里所能得到的最小费用。 写出 ** 状态转移方程 ** :$f_i=max\{f_j+(pre_i-pre_i+i-j-1-L)^2\}$ ,其中 $pre_i$ 表示前 $i$ 个数的前缀和。 换元试图简化状态转移方程式: 令 $s_i=pre_i+i,L'=L+1$,则 $f_i=f_j+(s_i-s_j-L')^2$,展开,移项得 $f_i=f_j+(s_i-s_j-L')^2$ $f_i+2\times s_i\times (s_j+L')=f_j+s_i^2+(s_j+L')^2$ 我们观察到,式子的右端的所有项都只和 $i$ 有关或只和 $j$ 有关,式子左端的第一项是我们要求的目标值,式子左端的其余项都同时和 $i$ 和 $j$ 有关。我们将这个式子看作一条直线的函数解析式,形如 $b+k\times x=y$ ,和上式一一对应。我们发现如果我们要最小化 $f_i$ ,也就是说要最小化这个直线的截距,而对于每个确定的 $i$,这个直线的斜率 $s_i$ 都是确定的。  如图,我们将这个斜率固定的直线从下往上平移,直到有一个点在这条直线上,然后将新的点加入点集,这样肯定能保证所有的直线的斜率都是单调递升的(因为如果新的直线斜率小于斜率最大的直线,那么其一定不成被选择成为新的决策),所以我们相当于维护了一个下凸包。(如果求的是 $\max$ 那么就要维护一个 ** 上凸包 ** 。这种东西要具体情况具体分析,如果直线的斜率不满足单调性,那就要维护整个凸包/二分等奇技淫巧。) 可以用单调队列维护下凸包。 ### 几道练习题: [luogu P4072 [SDOI2016] 征途](https://www.luogu.org/problemnew/show/P4072) [luogu P2120 [ZJOI2007] 仓库建设](https://www.luogu.org/problemnew/show/P2120) [luogu P3628 [APIO2010] 特别行动队](https://www.luogu.org/problemnew/show/P3628) [bzoj 4709 [Jsoi2011] 柠檬](https://www.lydsy.com/JudgeOnline/problem.php?id=4709) [CF311B Cats Transport](http://codeforces.com/problemset/problem/311/B) [luogu P4027 [NOI2007] 货币兑换](https://www.luogu.org/problemnew/show/P4027) Loading
docs/dp/optimization.md +108 −75 Original line number Diff line number Diff line Loading @@ -2,6 +2,114 @@ By [hsfzLZH1](https://github.com/hsfzLZH1) 本章主要讲解动态规划的几种基础 ** 优化 ** 方法。 ## 二进制优化解多重背包 ### 例题 [经典问题-多重背包](https://oi-wiki.org/dp/backpack/#_3) 题目大意:有 $n$ 种物品,每种物品有 $a_i$ 件,购买一件这种物品的费用为 $c_i$,价值为 $v_i$。有一个容量为 $t$ 的背包,现在让你找到最优的一种方案,使得装入背包的物品的总价值最大。 考虑常规的动规方式,定义 $f_{i,j}$ 为当前考虑到第 $i$ 个物品,背包容量为 $j$ 所能获得的最大价值。 状态转移方程为, $f_{i,j}=\max\{f_{i-1,j},f_{i-1,j-c_i}+v_i\}$。 对于 ** 每件 ** 物品,都要这样循环一次,时间复杂度为 $t\times \sum_{i=1}^n a_i$,某些时候可能不可接受,需要优化。 考虑这样一种情况,如果我们有 $17$ 个硬币,要去买 $1$ 到 $17$ 元钱的物品,只需将这些硬币打包成 $1,2,4,8$ 和 $2$ 这样的几包。前面的 $4$ 包能保证覆盖 $1$ 到 $15$ 所有的情况,最后一包在之前的基础上再加上一个值,能保证实现支付的时候取整包,肯定能保证支付。这就是二进制优化的原理和基本思想。 用上述的方法,就可以把 $k$ 件相同的物品看作是 $O(log_2 k)$ 件物品了。优化后 ## 代码实现 ```cpp for(int i=1;i<=n;i++) { scanf("%d",a+i); tot+=c[i]*a[i]; for(int j=1;j<=a[i];j*=2)if(a[i]>=j)a[i]-=j,v[++cur]=c[i]*j; if(a[i])v[++cur]=c[i]*a[i]; } for(int i=1;i<=cur;i++)for(int j=m;j>=v[i];j--)if(f[j-v[i]])f[j]=true; ``` ## 几道练习题 [HDU 2844 Coins](http://acm.hdu.edu.cn/showproblem.php?pid=2844) ## 单调队列&单调栈优化 学习本节前,请务必先学习 [单调队列](https://oi-wiki.org/ds/monotonous-queue/)。 ### 例题 [CF372C Watching Fireworks is Fun](http://codeforces.com/problemset/problem/372/C) 题目大意:城镇中有 $n$ 个位置,有 $m$ 个烟花要放。第 $i$ 个烟花放出的时间记为 $t_i$,放出的位置记为 $a_i$。如果烟花放出的时候,你处在位置 $x$,那么你将收获 $b_i-|a_i-x|$ 点快乐值。 初始你可在任意位置,你每个单位时间可以移动不大于 $d$ 个单位距离。现在你需要最大化你能获得的快乐值。 设 $f_{i,j}$ 表示在放第 $i$ 个烟花时,你的位置在 $j$ 所能获得的最大快乐值。 写出 ** 状态转移方程 ** :$f_{i,j}=\max\{f_{i-1,k}+b_i-|a_i-j|\}$ 这里的 $k$ 是有范围的,$j-(t_{i+1}-t_i)\times d\le k\le j+(t_{i+1}-t_i)\times d$。 我们尝试将状态转移方程进行变形: 由于 $\max$ 里出现了一个确定的常量 $b_i$,我们可以将它提到外面去。 $f_{i,j}=\max\{f_{i-1,k}+b_i+|a_i-j|\}=\max\{f_{i-1,k}+|a_i-j|\}+b_i$ 如果确定了 $i$ 和 $j$ 的值,那么 $|a_i-j|$ 的值也是确定的,也可以将这一部分提到外面去。 最后,式子变成了这个样子:$f_{i,j}=\max\{f_{i-1,k}+|a_i-j|\}+b_i=\max\{f_{i-1,k}\}+|a_i-j|+b_i$ 看到这一熟悉的形式,我们想到了什么?** 单调队列优化 **。由于最终式子中的 $\max$ 只和上一状态中连续的一段的最大值有关,所以我们在计算一个新的 $i$ 的状态值时候只需将原来的 $f_{i-1}$ 构造成一个单调队列,并维护单调队列,使得其能在均摊 $O(1)$ 的时间复杂度内计算出 $\max\{f_{i-1,k}\}$ 的值,从而根据公式计算出 $f_{i,j}$ 的值。 总的时间复杂度为 $O(n\times m)$。 讲完了,让我们归纳一下单调队列优化动态规划问题的基本形态:当前状态的所有值可以从上一个状态的某个连续的段的值得到,要对这个连续的段进行 RMQ 操作,相邻状态的段的左右区间满足非降的关系。 ### 几道练习题: [luogu P1886 滑动窗口](https://www.luogu.org/problemnew/show/P1886) [luogu P2254 [NOI2005] 瑰丽华尔兹](https://www.luogu.org/problemnew/show/P2254) [luogu P2569 [SCOI2010] 股票交易](https://www.luogu.org/problemnew/show/P2569) ## 斜率优化 ### 例题 [luogu P3195 [HNOI2008]玩具装箱TOY](https://www.luogu.org/problemnew/show/P3195) 令 $f_i$ 表示前 $i$ 个物品,随意分组装在任意多个容器里所能得到的最小费用。 写出 ** 状态转移方程 ** :$f_i=max\{f_j+(pre_i-pre_i+i-j-1-L)^2\}$ ,其中 $pre_i$ 表示前 $i$ 个数的前缀和。 换元试图简化状态转移方程式: 令 $s_i=pre_i+i,L'=L+1$,则 $f_i=f_j+(s_i-s_j-L')^2$,展开,移项得 $f_i=f_j+(s_i-s_j-L')^2$ $f_i+2\times s_i\times (s_j+L')=f_j+s_i^2+(s_j+L')^2$ 我们观察到,式子的右端的所有项都只和 $i$ 有关或只和 $j$ 有关,式子左端的第一项是我们要求的目标值,式子左端的其余项都同时和 $i$ 和 $j$ 有关。我们将这个式子看作一条直线的函数解析式,形如 $b+k\times x=y$ ,和上式一一对应。我们发现如果我们要最小化 $f_i$ ,也就是说要最小化这个直线的截距,而对于每个确定的 $i$,这个直线的斜率 $s_i$ 都是确定的。  如图,我们将这个斜率固定的直线从下往上平移,直到有一个点在这条直线上,然后将新的点加入点集,这样肯定能保证所有的直线的斜率都是单调递升的(因为如果新的直线斜率小于斜率最大的直线,那么其一定不成被选择成为新的决策),所以我们相当于维护了一个下凸包。(如果求的是 $\max$ 那么就要维护一个 ** 上凸包 ** 。这种东西要具体情况具体分析,如果直线的斜率不满足单调性,那就要维护整个凸包/二分等奇技淫巧。) 可以用单调队列维护下凸包。 ### 几道练习题: [luogu P4072 [SDOI2016] 征途](https://www.luogu.org/problemnew/show/P4072) [luogu P2120 [ZJOI2007] 仓库建设](https://www.luogu.org/problemnew/show/P2120) [luogu P3628 [APIO2010] 特别行动队](https://www.luogu.org/problemnew/show/P3628) [bzoj 4709 [Jsoi2011] 柠檬](https://www.lydsy.com/JudgeOnline/problem.php?id=4709) [CF311B Cats Transport](http://codeforces.com/problemset/problem/311/B) [luogu P4027 [NOI2007] 货币兑换](https://www.luogu.org/problemnew/show/P4027) ## 四边形不等式优化 ### 例题 [luogu P1880 [NOI1995]石子合并](https://www.luogu.org/problemnew/show/P1880) Loading Loading @@ -117,78 +225,3 @@ for(int i=n;i>=1;i--) ### 参考资料 [NOIAu 的CSDN 博客](https://blog.csdn.net/noiau/article/details/72514812) ## 单调队列&单调栈优化 学习本节前,请务必先学习 [单调队列](https://oi-wiki.org/ds/monotonous-queue/)。 ### 例题 [CF372C Watching Fireworks is Fun](http://codeforces.com/problemset/problem/372/C) 题目大意:城镇中有 $n$ 个位置,有 $m$ 个烟花要放。第 $i$ 个烟花放出的时间记为 $t_i$,放出的位置记为 $a_i$。如果烟花放出的时候,你处在位置 $x$,那么你将收获 $b_i-|a_i-x|$ 点快乐值。 初始你可在任意位置,你每个单位时间可以移动不大于 $d$ 个单位距离。现在你需要最大化你能获得的快乐值。 设 $f_{i,j}$ 表示在放第 $i$ 个烟花时,你的位置在 $j$ 所能获得的最大快乐值。 写出 ** 状态转移方程 ** :$f_{i,j}=\max\{f_{i-1,k}+b_i-|a_i-j|\}$ 这里的 $k$ 是有范围的,$j-(t_{i+1}-t_i)\times d\le k\le j+(t_{i+1}-t_i)\times d$。 我们尝试将状态转移方程进行变形: 由于 $\max$ 里出现了一个确定的常量 $b_i$,我们可以将它提到外面去。 $f_{i,j}=\max\{f_{i-1,k}+b_i+|a_i-j|\}=\max\{f_{i-1,k}+|a_i-j|\}+b_i$ 如果确定了 $i$ 和 $j$ 的值,那么 $|a_i-j|$ 的值也是确定的,也可以将这一部分提到外面去。 最后,式子变成了这个样子:$f_{i,j}=\max\{f_{i-1,k}+|a_i-j|\}+b_i=\max\{f_{i-1,k}\}+|a_i-j|+b_i$ 看到这一熟悉的形式,我们想到了什么?** 单调队列优化 **。由于最终式子中的 $\max$ 只和上一状态中连续的一段的最大值有关,所以我们在计算一个新的 $i$ 的状态值时候只需将原来的 $f_{i-1}$ 构造成一个单调队列,并维护单调队列,使得其能在均摊 $O(1)$ 的时间复杂度内计算出 $\max\{f_{i-1,k}\}$ 的值,从而根据公式计算出 $f_{i,j}$ 的值。 总的时间复杂度为 $O(n\times m)$。 讲完了,让我们归纳一下单调队列优化动态规划问题的基本形态:当前状态的所有值可以从上一个状态的某个连续的段的值得到,要对这个连续的段进行 RMQ 操作,相邻状态的段的左右区间满足非降的关系。 ### 几道练习题: [luogu P1886 滑动窗口](https://www.luogu.org/problemnew/show/P1886) [luogu P2254 [NOI2005] 瑰丽华尔兹](https://www.luogu.org/problemnew/show/P2254) [luogu P2569 [SCOI2010] 股票交易](https://www.luogu.org/problemnew/show/P2569) ## 斜率优化 ### 例题 [luogu P3195 [HNOI2008]玩具装箱TOY](https://www.luogu.org/problemnew/show/P3195) 令 $f_i$ 表示前 $i$ 个物品,随意分组装在任意多个容器里所能得到的最小费用。 写出 ** 状态转移方程 ** :$f_i=max\{f_j+(pre_i-pre_i+i-j-1-L)^2\}$ ,其中 $pre_i$ 表示前 $i$ 个数的前缀和。 换元试图简化状态转移方程式: 令 $s_i=pre_i+i,L'=L+1$,则 $f_i=f_j+(s_i-s_j-L')^2$,展开,移项得 $f_i=f_j+(s_i-s_j-L')^2$ $f_i+2\times s_i\times (s_j+L')=f_j+s_i^2+(s_j+L')^2$ 我们观察到,式子的右端的所有项都只和 $i$ 有关或只和 $j$ 有关,式子左端的第一项是我们要求的目标值,式子左端的其余项都同时和 $i$ 和 $j$ 有关。我们将这个式子看作一条直线的函数解析式,形如 $b+k\times x=y$ ,和上式一一对应。我们发现如果我们要最小化 $f_i$ ,也就是说要最小化这个直线的截距,而对于每个确定的 $i$,这个直线的斜率 $s_i$ 都是确定的。  如图,我们将这个斜率固定的直线从下往上平移,直到有一个点在这条直线上,然后将新的点加入点集,这样肯定能保证所有的直线的斜率都是单调递升的(因为如果新的直线斜率小于斜率最大的直线,那么其一定不成被选择成为新的决策),所以我们相当于维护了一个下凸包。(如果求的是 $\max$ 那么就要维护一个 ** 上凸包 ** 。这种东西要具体情况具体分析,如果直线的斜率不满足单调性,那就要维护整个凸包/二分等奇技淫巧。) 可以用单调队列维护下凸包。 ### 几道练习题: [luogu P4072 [SDOI2016] 征途](https://www.luogu.org/problemnew/show/P4072) [luogu P2120 [ZJOI2007] 仓库建设](https://www.luogu.org/problemnew/show/P2120) [luogu P3628 [APIO2010] 特别行动队](https://www.luogu.org/problemnew/show/P3628) [bzoj 4709 [Jsoi2011] 柠檬](https://www.lydsy.com/JudgeOnline/problem.php?id=4709) [CF311B Cats Transport](http://codeforces.com/problemset/problem/311/B) [luogu P4027 [NOI2007] 货币兑换](https://www.luogu.org/problemnew/show/P4027)