https://www.luogu.com.cn/problem/P4767
清一色的四边形不等式......
其实分治优化决策单调性DP也是可以的
令(f(i,j))表示前(j)个村庄,建(i)个邮局,每个村庄和最近的邮局之间所有距离最小总和是多少。
再设(w(i,j))表示在第(i)个到第(j)个村庄之间建一个邮局最小的距离总和。
根据中位数定理,发现邮局一定建在([i,j])的中间。
于是维护个前缀和啥的就可以(V^2)处理出(w)。
然后考虑DP的转移,枚举最后一个邮局建在哪个区间内,即
[f(i,j) = maxlimits_{1 le k le j} { f(i-1,k-1)+w(k,j)}
]
特别的,如果(j<i),那么(f(i,j) = infty)
发现(f(i,...))只会从(f(i-1,...))转移,瞎猜一个决策点也满足单调性,于是分治优化一层的转移即可。
虽然比四边形不等式多一个(log)...但好像跑起来也差不多)
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
const int inf = 1e9;
int cost[N][N], s[N][N], x[N];
void solve(int *f, int *g, int l, int r, int pl, int pr) {
if (l > r || pl > pr) {
return;
}
auto weight = [&](int i, int j) {
return cost[i][j];
};
if (pl == pr) {
for (int i = l; i <= r; i++) {
f[i] = g[pl - 1] + weight(pl, i);
}
return;
}
int mid = (l + r) >> 1;
int pmid = -1;
int mn = inf * 2;
for (int i = pl; i <= min(mid, pr); i++) {
if (g[i - 1] + weight(i, mid) < mn) {
mn = g[i - 1] + weight(i, mid);
pmid = i;
}
}
f[mid] = mn;
solve(f, g, l, mid - 1, pl, pmid);
solve(f, g, mid + 1, r, pmid, pr);
}
int dp[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> x[i];
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
s[i][j] = s[i][j - 1] + x[j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
int mid = (i + j) >> 1;
cost[i][j] = x[mid] * (mid - i + 1) - s[i][mid] + s[mid][j] - x[mid] * (j - mid + 1);
}
}
dp[1][0] = inf;
for (int i = 1; i <= n; i++) {
dp[1][i] = cost[1][i];
}
for (int i = 2; i <= m; i++) {
solve(dp[i], dp[i - 1], 1, n, 1, n);
for (int j = 0; j < i; j++) {
dp[i][j] = inf;
}
}
cout << dp[m][n] << '
';
return 0;
}