题解 AcWing1326 【军训队列】
前文:这是自己的思路过的第一道dp啊,太感动了
题目描述:
有 \(n\) 名学生参加军训,军训的一大重要内容就是走队列,而一个队列的不整齐程度是该队中最高的学生的身高与最矮的学生的身高差值的平方。
现在要将 \(n\) 名参加军训的学生分成 \(k\) 个队列,每个队列的人数可以是任意非负整数。
在安排队列时希望所有队列的不整齐度之和尽量小,请问不整齐度之和最小可以是多少?
算法:贪心+dp
做法:
1.贪心
可以看出这些数先排序后再进行分队会更优,为什么因为如果选取的两个队列有交集,那么可以把交集里的数放进任意一个队列里,这样差值会更小。
如:\(1\) \(5\) \(3\) \(7\) 四个数要分为两个队列,我将 \(1\) 和 \(5\) 分为一队,\(3\) 和 \(7\) 分为另一对,这样差值的平方和是 \(32\),这里 \(3\) 比 \(5\) 小,出现了交集,那么我可以把5放进3和7的队列里这样第一个队列只剩下 \(1\),第二个队列有 \(3\)、\(5\)、\(7\) 三个数,差值的平方和变为 \(16\),比原来的 \(32\) 小。
2.dp
我们用 \(f(i, j)\) 表示将前 \(i\) 个人分为 \(j\) 个队列的所有方案中的最小值。
我们考虑最后对 \(f(i, j)\) 有影响的因素,那就是最后一个队列怎么安排。
我们考虑枚举最后一个队列的开始位置(结束位置当然是 \(i\)),那么可以得出枚举的范围是 \(j\) 到 \(i\) 。(如果前 \(j-1\) 个人每人一个队列,后面 \(i-j+1\) 个人都放进一个队列,开始位置就是 \(j\), 如果 \(i\) 一个人一个队列,那么开始的位置就是 \(i\))
用 \(k\) 表示最后一个队列开始的位置,求出的和就是前面k-1个人分为j-1个队列的不整齐度最小值加上最后一个队列的不整齐度。
那么就可以得到(\(k\) 从 \(j\) 到 \(i\)):
\(f(i, j) = \min(f(i, j),f(k-1, j-1) + (h[i] - h[k]) * (h[i]-h[k]))\)
最后的 \(f(n, k)\) 就是答案。
时间复杂度:\(O(n^3)\)
虽然 \(n \leq 500\) 但是有 \(j \leq i\) ,复杂度就被砍掉一半了,所以是可以过的。这题可以用斜率优化把复杂度降到 \(O(nlogn)\),但是我不会。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 505;
int dp[N][N];
int sqr(int x) {
return x * x;
}
int main() {
int n, m;
int q[N];
scanf("%d %d", &n, &m);
for (int i=1; i<=n; i++)
scanf("%d", &q[i]);
sort(q+1, q+n+1);
for (int i=1; i<=n; i++) {
for (int j=1; j<=m; j++) {
if (j == 1) {
dp[i][j] = sqr(q[i] - q[1]);
continue;
}
dp[i][j] = 0x7fffffff;
for (int k=j; k<=i; k++)
dp[i][j] = min (dp[i][j], dp[k-1][j-1] + sqr(q[i] - q[k]));
}
}
printf("%d", dp[n][m]);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步