1 条题解
-
0
题目理解
我们有 座山,第 座山的高度为 。我们希望在尽可能少的挖掘工作量下,让尽可能多的山成为"山峰"(即比相邻的山严格高)。
对于每个 (),我们需要找到建造 座房子的最小挖掘时间。
问题分析
这是一个动态规划问题。关键观察是:
- 山峰不能相邻(因为一个山峰需要比两边都高)
- 当我们决定在某位置建房子时,可能需要降低相邻山的高度
动态规划状态设计
设 表示考虑前 座山,第 座山建了房子,总共建了 座房子的最小代价。
设 表示考虑前 座山,总共建了 座房子的最小代价(用于辅助计算)。
状态转移
对于每个位置 和房子数量 :
-
如果第 座山建房子:
- 需要确保 和
- 代价包括:
- 降低 的代价:
- 降低 的代价:
- 有两种情况:
- 从 转移(即前 座山的最优解)
- 从 转移,但要考虑 对 的影响
-
更新 数组:
代码解析
#include <bits/stdc++.h> using namespace std; const int N = 5010; int n, a[N], f[N][N / 2], g[N]; int main() { cin >> n; memset(f, 0x3f, sizeof(f)); memset(g, 0x3f, sizeof(g)); g[0] = f[0][0] = 0; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= (i + 1) / 2; j++) { // 情况1:从g[j-1]转移 f[i][j] = g[j - 1] + max(0, a[i - 1] - a[i] + 1) + max(0, a[i + 1] - a[i] + 1); // 情况2:从f[i-2][j-1]转移(需要i>2) if (i > 2) f[i][j] = min(f[i][j], f[i - 2][j - 1] + max(0, min(a[i - 2] - 1, a[i - 1]) - a[i] + 1) + max(0, a[i + 1] - a[i] + 1)); } // 更新g数组 if (i > 1) for (int j = 0; j <= (i + 1) / 2; j++) g[j] = min(g[j], f[i - 2][j]); } // 输出答案 for (int i = 1; i <= (n + 1) / 2; i++) cout << min(f[n][i], min(f[n - 1][i], g[i])) << " \n"[i == (n + 1) / 2]; return 0; }关键点解释
-
边界处理:
- 对于 ,只需要考虑右边邻居
- 对于 ,只需要考虑左边邻居
- 代码中的
max(0, ...)确保只计算需要降低的高度
-
数组的作用:
- 记录前 座山的最优解
- 避免相邻山峰冲突
-
最终答案:
- 可能结束于第 座山、第 座山,或者更早
- 取三者最小值确保找到全局最优
时间复杂度
- 状态数:
- 总时间复杂度:
- 空间复杂度:(可优化到 )
算法标签
- 动态规划
- 序列处理
- 最优化
这个解法通过巧妙的动态规划状态设计,高效地解决了山峰建造的最优化问题。
- 1
信息
- ID
- 3616
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者