Description
A先生有很多双筷子。确切的说应该是很多根,因为筷子的长度不一,很难判断出哪两根是一双的。这天,A先生家里来了 个客人,A先生留下他们吃晚饭。加上A先生,A夫人和他们的孩子小A,共 个人。每人需要用一双筷子。A先生只好清理了一下筷子,共 根,长度为 .现在他想用这些筷子组合成 双,使每双的筷子长度差的平方和最小。(怎么不是和最小??这要去问A先生了,呵呵)
Input Format
输入文件共有两行,第一行为两个用空格隔开的整数,表示
第二行共有N个用空格隔开的整数,为
.每个整数为1~50之间的数。
Output Format
输出文件仅一行。如果凑不齐K+3双,输出-1,否则输出长度差平方和的最小值。
Sample Input
10 1
1 1 2 3 3 3 4 6 10 20
Sample Output
5
Hint
第一双
第二双
第三双
第四双
分析:
这道题看到就知道没有别的办法了,只能DP,贪心选择显然有问题。
排序是必须的,难点在于状态定义,很容易想到的是令 为从 到 选择 双筷子的最小平方差之和。
可是这样明显转移困难,考虑加一个参数。
令 为区间 到 内选择 双筷子的代价(平方差之和)。
显然这样转移也比较困难,当 时根本无法转移(除非你想每个测试点都TLE)。
令 表示选择 双筷子最小的代价?
还是没法转移……
于是我就卡壳了……(原谅本蒟蒻水平有限)。
然后去问老师,老师开始把状态定义成第三种状态,也是没法转移,于是老师加了个限制:
令 表示前 个数中取 双筷子最小的代价
于是状态变得有序化了,我就自己推导出了决策与方程。
决策:
对于每个 ,决策不外乎就是让 和 作为一双筷子和放弃 双筷子。
方程:
阶段:
以前 个数作为一个阶段。
综上,时间复杂度为
,所以
的数据明显放水。
:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int a[105], dp[105][105], n, k;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++)
scanf("%d", a + i);
sort(a + 1, a + n + 1);//别忘了排序
k += 3;
if (k > (n >> 1))//如果根本不够分K+3双,输出-1
{
puts("-1");
return 0;
}
for (int i = 1; i <= 51; i ++)//初始化QAQ
for (int j = 0; j < 2 * i; j ++)//如果该区间内不够分i双筷子,那么初始化为INF
dp[j][i] = 0x3fffffff;//别整7f,一不小心就炸了
for (int i = 2; i <= n; i ++)
for (int j = 1; j <= (i >> 1); j ++)//最多只能分i/2双筷子
{
dp[i][j] = dp[i - 2][j - 1] + (a[i] - a[i - 1]) * (a[i] - a[i - 1]);//方程,如果取a[i]与a[i-1]作为一双筷子
if (dp[i - 1][j] < dp[i][j]) dp[i][j] = dp[i - 1][j];//如果放弃第i根筷子,那么取dp[i-1][j]
}
printf("%d", dp[n][k]);
return 0;//其实代码不长,主要是状态定义(这是一道区间DP)
}