概念
和平行四边形优化一样,往往起到对DP降维的作用。
使用条件是满足决策单调性。
关于决策单调性的定义
截图来自参考博客
四边形不等式的运用参考博客
斜率DP的详细讲解参考博客
补一个凸包的板子参考博客
另外,四边形不等式只是验证决策单调性,而决定取上还是下凸包则由斜率不等式的推导决定。
一道与主题不相关的例题
原题地址
代码:
目前找到了两种解法
代码来源
这个解法有传统dp的意味,即得到dp[i]有两种方法,一种是重新划出一个长度为k的序列,另一种是将第i个元素并入上一个序列中。
但学习了斜率优化dp后,更推崇命题人给的解法
代码来源
这有斜率dp的一个思想: 决策点(即j点)与目标点(即i点),在状态转移方程中将含有i的点和含有j的点分离。
当不能分离的时候,就可以考虑斜率dp了。
Necklace
原题地址
代码
题目大意:将一个有k个元素的环分成k个部分,每个部分的权值是其元素和的平方,问权值总和最小值。
四边形不等式:
淘汰条件见参考博客
显然是一个下凸包优化
乱写的推导过程
#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 202
#define INF 0x3f3f3f3f
#define Re register ll
int T, n, k, tl = 0, h = 0, Q[maxn];
ll dp[maxn][maxn], a[maxn], sum[maxn], b[maxn];
//inline int get(int j) { return (j - 1) % n + 1; }
inline ll X(Re j) { return sum[j]; }
inline ll Y(Re j, Re v) { return dp[j][v] + sum[j] * sum[j]; }
inline long double slope(Re i, Re j, Re v) { return (long double)(Y(j, v - 1) - Y(i, v - 1)) / (X(j) - X(i)); }
int main()
{
ios::sync_with_stdio(false);
int i, j, r, p;
cin >> T;
while (T--)
{
ll ans = INF;
tl = h = 0;
Q[++tl] = 0;
cin >> n >> k;
for (i = 1; i <= n; i++)cin >> a[i];
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
b[j] = a[((j + i - 1) > n ? (j + i - 1 - n) : (j + i - 1))];
}
for (j = 1; j <= n; j++) sum[j] = sum[j - 1] + b[j];
for (j = 1; j <= n; j++) dp[j][1] = sum[j] * sum[j];
for (p = 2; p <= k; p++)
{
tl = h = 0;
Q[++tl] = p - 1;
for (j = p; j <= n; j++)
{
while (h < tl && slope(Q[h], Q[h + 1], p) <= 2 * sum[j]) ++h;
dp[j][p] = dp[r = Q[h]][p - 1] + (sum[r] - sum[j])*(sum[r] - sum[j]);
while (h < tl && slope(Q[tl - 1], Q[tl], p) >= slope(Q[tl - 1], j, p)) --tl;
Q[++tl] = j;
}
}
ans = min(ans, dp[n][k]);
}
cout << ans << endl;
}
return 0;
}
神奇的是代码一直TLE,感觉和参考博客的差不多,唯一合理的解释就是调用了函数???
另一道偏题的题目
荡开一笔,不细讲。这道题展示了传统dp的思维方式,作为对比放在这里。
详细讲解