【知识】四边形不等式
四边形不等式
定义:
对于二维函数
性质:
满足
证明:
-
取
令
,则有即
整理可得,
-
取
同理可得,
所以 :
-
取
则
整理可得,
-
取
同理可得,
所以:
证毕!
为啥叫四边形不等式呢?如下图
易证
DP 优化:
考虑一种决策型动态规划问题,其状态转移方程如下:
这种形式的转移方程通常出现在以下类型的问题中:给定
为了优化这个问题,引入决策点的概念。我们定义
对于这类问题,一般的做法是直接采用朴素的
Property 1:
如果
Property 2:
如果存在一个
特别要注意的是,
证明:
由于
同时,由于
这可以推导出:
将上述不等式与原不等式相加,得到:
如何优化 DP ?
首先,我们设定初始时每个
基于性质 2,如果某个
为了进一步优化,我们可以维护一个单调队列,其中每个元素表示一个三元组
另外,也可以采用分治法来进行优化,这样的时间复杂度同样是
题目梳理:
-
P3195 [HNOI2008]玩具装箱
考虑一个
的 dp。设
表示前 个玩具的最小总费用, 表示前 个玩具的长度和,即前缀和。有转移方程:
。在这题中前文的 即后面的 ,我们只需要证明 v 满足四边形不等式即可。Code
#include <bits/stdc++.h> using namespace std; #define int long long const int MAXN = 50050; int N, L, dp[MAXN], sum[MAXN], f[MAXN], g[MAXN], h, t, Q[MAXN]; inline double calc(int j1, int j2){ return (double) (dp[j2] + g[j2] - dp[j1] - g[j1]) / (f[j2] - f[j1]); } #undef int int main(){ cin >> N >> L; for(int i = 1; i <= N; i++) { cin >> sum[i]; sum[i] += sum[i - 1]; f[i] = sum[i] + i; g[i] = (f[i] + L + 1) * (f[i] + L + 1); } g[0] = (L + 1) * (L + 1); for(int i = 1; i <= N; i++){ while(h < t && calc(Q[h], Q[h + 1]) <= 2 * f[i]) h++; dp[i] = dp[Q[h]] + (f[i] - f[Q[h]] - L - 1) * (f[i] - f[Q[h]] - L - 1); while(h < t && calc(Q[t], i) < calc(Q[t - 1], Q[t])) t--; Q[++t] = i; } cout << dp[N]; return 0; }
-
P1912 [NOI2009] 诗人小G
很显然的 dp 方程:
其中
如果这个状态转移方程是决策单调的,那么可以直接上单调队列。
但是怎么证明呢?
我们只需证明函数
满足四边形不等式。尝试把左右两边统一化,简化式子,表示
设
则
所以原问题等价于证明
非严格单调递减。注意到有绝对值,我们分类讨论。
当
由于
当
证明过程同第一种情况,此处省略。
当
由于
恒大于 ,当
显然
,因为这是一个偶函数。所以
Code
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long double LD; const int N = 100010; int n, L, P; LD f[N]; char str[N][31]; int s[N], opt[N]; struct Node { int j, l, r; }q[N]; int hh, tt; LD val(int j, int i) { LD res = 1, a = abs(s[i] - s[j] + i - j - 1 - L); for (int i = 0; i < P; i ++ ) res *= a; return res + f[j]; } void insert(int i) { int pos = n + 1; while (hh <= tt && val(q[tt].j, q[tt].l) >= val(i, q[tt].l)) pos = q[tt -- ].l; if (hh <= tt && val(q[tt].j, q[tt].r) >= val(i, q[tt].r)) { int l = q[tt].l, r = q[tt].r; while (l < r) { int mid = l + r >> 1; if (val(q[tt].j, mid) >= val(i, mid)) r = mid; else l = mid + 1; } q[tt].r = r - 1; pos = r; } if (pos != n + 1) q[ ++ tt] = {i, pos, n}; } int main() { int T; scanf("%d", &T); while (T -- ) { scanf("%d%d%d", &n, &L, &P); for (int i = n; i >= 1; i -- ) scanf("%s", str[i]); for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + strlen(str[i]); hh = tt = 0; q[0] = {0, 1, n}; for (int i = 1; i <= n; i ++ ) { f[i] = val(q[hh].j, i), opt[i] = q[hh].j; if (q[hh].r == i) hh ++ ; q[hh].l = i + 1; insert(i); } if (f[n] > 1e18) puts("Too hard to arrange"); else { printf("%lld\n", (long long)f[n]); for (int i = n; i; i = opt[i]) { for (int j = i; j > opt[i]; j -- ) { printf("%s", str[j]); if (j != opt[i] + 1) printf(" "); } puts(""); } } puts("--------------------"); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!