【问题描述】
共N根筷子,长度为T1,T2,T3,……,TN。组成K+3对,使每双的筷子长度差的平方和最小。
【输入文件】
输入文件共有两行,第一行为两个用空格隔开的整数,表示N,K(1≤N≤100,0<K<50),第二行共有N个用空格隔开的整数,为Ti每个整数为1~50之间的数。
【输出文件】
输出文件仅一行。如果凑不齐K+3双,输出-1,否则输出长度差平方和的最小值。
【样例输入】
10 1
1 1 2 3 3 3 4 6 10 20
【样例输出】
5
可以说是有点魔性的动规了……
考虑完n支筷子不够排的情况后我们看够的情况。
首先需要一点贪心:先把筷子长度排序,这样可以保证对于每一根筷子,长度差最小的都在两侧(当然是不是最优解这个另算)
然后对于n支筷子,最多能组成n/2双筷子;如果用dp[i][j]表示前i支筷子组成j双筷子时的最优解,我们只需要考虑加入当前筷子后能否得到更优解。如果不加入当前筷子,dp[i][j]=dp[i-1][j];否则dp[i][j]=dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]).
即状态转移方程:dp[i][j]=min(dp[i-1][j],(dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1])));
dp数组初始化应该初始化为一个特别大的数……要不然你最后输出会魔性地全是0。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int n,k;
int a[105];
int dp[105][105];
int main()
{
scanf("%d%d",&n,&k);
if((2*k+6)>n)
{
printf("-1");
return 0;
}
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a,a+n+1);
memset(dp,999999,sizeof(dp));
for(int i=0;i<=n;i++)
dp[i][0]=0;
for(int i=2;i<=n;i++)
for(int j=1;j<=i/2;j++)
{
dp[i][j]=min(dp[i-1][j],(dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1])));
}
printf("%d",dp[n][k+3]);
return 0;
}