P4072 征途 Solution
斜率优化大水题。
首先看到方差,想到拆式子。(下面 表示每一段路程的长度)
令 ,则原式
考虑像方差这题的处理方式,将和压入状态,将平方和作为状态表示的答案,即设 表示当前枚举到第 段路径的右端点,为第 次停靠,当前的路径和为 的最小平方和,最后输出 即可,复杂度为 ,空间巨大。(其实这是看错题的写法)
得分 。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int M = 2e3 + 10;
int n, m, Max, a[N], sum[N], dp[N][N][M];
int main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i], sum[i] = sum[i - 1] + a[i];
memset(dp, 0x3f, sizeof(dp)); dp[1][0][0] = 0; Max = sum[n];
for (int i = 2; i <= n + 1; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = 1; k < i; ++k) {
int tmp = sum[i - 1] - sum[k - 1];
for (int p = Max; p >= tmp; --p) {
dp[i][j][p] = min(dp[i][j][p], dp[k][j - 1][p - tmp] + tmp * tmp);
}
}
}
}
int res = dp[0][0][0];
for (int p = 0; p <= Max; ++p) {
if (dp[n + 1][m][p] == dp[0][0][0]) continue;
res = min(res, dp[n + 1][m][p] * m - p * p);
}
cout << res << endl;
return 0;
}
接着考虑优化。重新看一遍题,发现最后的和一定为 ,所以把枚举和的一维去掉,最后输出 即可。
得分 。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
int n, m, Max, a[N], sum[N], dp[N][N];
int main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i], sum[i + 1] = sum[i] + a[i];
memset(dp, 0x3f, sizeof(dp)); dp[1][0] = 0; Max = sum[n + 1];
for (int i = 2; i <= n + 1; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = 1; k < i; ++k) {
int tmp = sum[i] - sum[k];
dp[i][j] = min(dp[i][j], dp[k][j - 1] + tmp * tmp);
}
}
}
cout << dp[n + 1][m] * m - Max * Max << endl;
return 0;
}
由于转移方程内含 与 下标的乘积项,考虑斜率优化。
下面令 。
首先,。
设 ,且 优于 。
则 。
把 有关的项移到左边 。
那么有 (除以负数要变号)。
维护 个单调队列就可以了,第 层的某个状态从 层转移而来即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e3 + 10;
int n, m, Max, a[N], l[N], r[N], sum[N], q[N][N], dp[N][N];
inline double slope(int j, int k1, int k2) {
return 1. * ((dp[k2][j] + sum[k2] * sum[k2]) - (dp[k1][j] + sum[k1] * sum[k1])) / (sum[k2] - sum[k1]);
}
signed main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i], sum[i] = sum[i - 1] + a[i];
Max = sum[n]; for (int i = 0; i <= m; ++i) l[i] = r[i] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
while (l[j - 1] < r[j - 1] && 2 * sum[i] >= slope(j - 1, q[j - 1][l[j - 1]], q[j - 1][l[j - 1] + 1])) ++l[j - 1];
dp[i][j] = dp[q[j - 1][l[j - 1]]][j - 1] + (sum[i] - sum[q[j - 1][l[j - 1]]]) * (sum[i] - sum[q[j - 1][l[j - 1]]]);
while (l[j] < r[j] && slope(j, q[j][r[j]], i) <= slope(j, q[j][r[j] - 1], q[j][r[j]])) --r[j];
q[j][++r[j]] = i;
}
}
cout << dp[n][m] * m - Max * Max << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现