描述
JR有很多双筷子。确切的说应该是很多根,因为筷子的长度不一,很难判断出哪两根是一双的。JR家里来了K个客人,JR留下他们吃晚饭。加上JR,JR的girl friend和JR的朋友内涵,共K+3个人。每人需要用一双筷子。JR只好清理了一下筷子,共N根,长度为T1,T2,T3,……,TN.现在他想用这些筷子组合成K+3双,使每双的筷子长度差的平方和最小。
输入格式
输入文件共有两行,第一行为两个用空格隔开的整数,表示N,K(1≤N≤100, 0<=K<50),第二行共有N个用空格隔开的整数,为Ti.每个整数为1~50之间的数。
输入
10 1
1 1 2 3 3 3 4 6 10 20
输出
5
今天终于领悟了dp的博大精深…….
这道题的状态并不难想到f[i][j]表示前i跟筷子拼成j双时的最小值;
但是在想方程的时候发现,
后效性消不掉
每次更新时都不能避免拆掉以前已经拼好的筷子;如果每一遍都扫着更新,那跟暴搜就没什么区别了;自然方程也是各种无语…想不到啊!!
后来发现是这样的:对于一个数,它的平方总是大于将它分为几部分后每部分的平方和;所以要想使平方和最小,可以将所有的高度sort一遍,每一根筷子都与其前一根或后面一根配对即可获得最优解(想不到反例,但不能严格证明,欢迎留言证明);所以对于每一次转移,我们可以选择第i根筷子选还是不选;
方程如下;
f[i][j]=min(f[i-1][j],f[i-2][j-1]+(high[i]-high[i-1])*(high[i]-high[i-1]));
初始化及细节在代码中说明
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int f[1000][1000];
int a[1000];
int n,k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>k;
k+=3;//not k+=k+3 神错误!!注意点@自己
for(int i=1;i<=n;i++)
cin>>a[i];
if(n<k*2){cout<<"-1";return 0;}//判断是否能够凑够;
sort(a+1,a+n+1);
memset(f,1,sizeof f);//初始化为最大值
for(int i=0;i<=n;i++) f[i][0]=0;//凑够0双代价为0,注意从0开始,否则1将无法转移
int i,j;
for(i=1;i<=n;i++)
for(j=1;(j<=k)&&(j<=i/2);j++)
f[i][j]=min(f[i-1][j],f[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]));
cout<<f[n][k];
return 0;
}