NC15052 求最值
题目
题目描述
给你一个长为 的序列
定义
是这样的一个函数
求最小的 的值,
输入描述
第一行一个数
之后一行 个数表示序列
输出描述
输出一行一个数表示答案
示例1
输入
4 1 0 0 -1
输出
1
备注
对于 的数据,
题解
方法一
知识点:分治,排序。
观察到, ,其中 指 的前缀和。
将其转换为平面上的点 则函数 为两点之间距离的平方,问题转换为平面最近点对问题。
这里一开始处理完已经是按 排序好的,直接开始分治。设区间左右端点为 ,若 ,则直接返回无穷大;若 ,则返回两点距离;若其他情况则先求得对半左右两个区间的临时最小值 ,用这个 作为阈值找到 轴坐标与区间分界坐标 差的平方小于 的点对,然后用朴素算法找到他们之间的最小值,过程中不断更新 。在最后一步,有个关键优化,先按 轴排序,这样对于一个定点找另一点时,若 轴差值平方大于等于目前 可以直接跳过,可以证明大部分情况可以将 降到 。
时间复杂度 平均: 最差:
空间复杂度
方法二
知识点:分治,排序。
方法二实际上是方法一的优化。通过 和 的 最小值,返回合并区间的 中 最小值,在合并时用归并把当前区间的区间按 排序,方便查找合并区间的最小值,是 。如果每次不排序,直接取点 按 排序,会重复排序,最差会 。
要注意的是,如果采用这种方法我们就必须是先排序好再确定可行下标,但是确定下标需要原来 处的 坐标,因此需要额外设置变量保存。
时间复杂度
空间复杂度
代码
方法一
#include <bits/stdc++.h> #define ll long long using namespace std; struct Point { ll x, y; }a[100007]; int c[100007]; ll sqr(ll x) { return x * x; } ll dist2(Point a, Point b) { return sqr(a.x - b.x) + sqr(a.y - b.y); } ll solve(int l, int r) { if (l == r) return ~(1LL << 63); if (l + 1 == r) return dist2(a[l], a[r]); int mid = l + r >> 1; ll ans = min(solve(l, mid), solve(mid + 1, r)); int cnt = 0; for (int i = l;i <= r;i++) if (sqr(a[i].x - a[mid].x) < ans) c[cnt++] = i; sort(c, c + cnt, [&](int p, int q) {return a[p].y < a[q].y;}); for (int i = 0;i < cnt;i++) { for (int j = i + 1;j < cnt;j++) { if (sqr(a[c[i]].y - a[c[j]].y) >= ans) break; ans = min(ans, dist2(a[i], a[j])); } } return ans; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; for (int i = 1;i <= n;i++) a[i].x = i, cin >> a[i].y, a[i].y += a[i - 1].y; cout << solve(1, n) << '\n'; return 0; }
方法二
#include <bits/stdc++.h> #define ll long long using namespace std; struct Point { ll x, y; }a[100007], b[100007]; ll c[100007]; ll sqr(ll x) { return x * x; } ll dist2(Point a, Point b) { return sqr(a.x - b.x) + sqr(a.y - b.y); } ll solve(int l, int r) { if (l == r) return ~(1LL << 63); if (l + 1 == r) return dist2(a[l], a[r]); int mid = l + r >> 1; ll midx = a[mid].x; ll ans = min(solve(l, mid), solve(mid + 1, r)); merge(a + l, a + mid + 1, a + mid + 1, a + r + 1, b + l, [&](Point a, Point b) {return a.y < b.y;}); for (int i = l;i <= r;i++) a[i] = b[i]; int cnt = 0; for (int i = l;i <= r;i++) if (sqr(a[i].x - midx) < ans) c[cnt++] = i; for (int i = 0;i < cnt;i++) { for (int j = i + 1;j < cnt;j++) { if (sqr(a[i].y - a[j].y) >= ans) break; ans = min(ans, dist2(a[i], a[j])); } } return ans; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; for (int i = 1;i <= n;i++) a[i].x = i, cin >> a[i].y, a[i].y += a[i - 1].y; cout << solve(1, n) << '\n'; return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16406499.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧