题目描述
佳佳与常人不同,吃饭用三只筷子,两根短的加一根比较长的。两只短的筷子的长度应该尽可能接近,但是最长的那根长度是无所谓的。如果一套筷子的长度分别是a,b,c(a<=b<=c),则用(a-b)*(a-b)的值表示这套筷子的质量,这个值越小,这套筷子的质量越高。
佳佳请朋友吃饭,并准备为每人准备一套这种特殊的筷子。佳佳有n(n<=5000)只筷子,他希望找到一种办法搭配好k套筷子,使得这些筷子的质量之和最小。

关于输入
第一行是两个整数n和k(n<=5000,3*k<=n)
第二行是n个整数表示筷子的长度
关于输出
输出一个整数,表示筷子质量和的最小值

例子输入
5 1
1 3 4 7 10
例子输出
1
提示信息
从小到大排序后从后向前递推

题目分析:典型的动态规划题目,状态的设置很好想到:dp[i][j]表示用前i根筷子搭配出j套筷子的最小质量和。很好想到(题目也给出了提示信息)可以先排序,想要质量之和最小,每次一定会选择排序后的相邻的筷子。
难点在于状态转移方程:如果我们不选择当前第i根筷子,dp[i][j]=dp[i-1][j]。如果我们选择当前的筷子,那这根筷子是作为最长的还是最短的呢?如果作为最长的,那么我们需要枚举前面的状态找到可能的最小值,可就是还需要遍历找到dp[m][j-1]+(chopsticks[m+1]-chopsticks[m+2])2。如果这跟是作为短的筷子,那么我们要确保后面还有比当前长的,就需要从后往前枚举,也就是让chopsticks从大到小排序,这时dp[i][j]=dp[i-2][j-1]+(chopsticks[i]-chopsticks[i-1])2,这样会简单一些,代码是按照这个思路写出的。

#include <algorithm>
#include <cstring>
using namespace std;
int Square(int x) {
	return (x * x);
}
int dp[5005][5005], chopsticks[5005];
bool cmp(int a, int b) {
	return a > b;
}
int main() {
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; i++) cin >> chopsticks[i];
	memset(dp, 0x3f, sizeof(dp));
	sort(chopsticks + 1, chopsticks + 1 + n, cmp);
	for (int i = 1; i <= n; i++) dp[i][0] = 0;
	for (int i = 3; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (i < 3 * j) break;
			dp[i][j] = min(dp[i - 1][j], dp[i - 2][j - 1] + Square(chopsticks[i - 1] - chopsticks[i]));
		}
	}
	cout << dp[n][k] << endl;
	return 0;
}```
posted on 2024-12-27 10:56  new_era_coder  阅读(92)  评论(0)    收藏  举报