题目要你求从n个筷子(升序)中选出k+8组,每组3个筷子且筷子大小要a<=b<=c,然后每组筷子中最短的两根(a-b)2表示为该组的值,求选出k+8组筷子时的最小值是多少。
那么很明显,筷子由于升序,若要两根筷子差的平方最小的时候,肯定是选择相邻的两根筷子,然后再选一根比前面两根大的作为第三根。
首先要将数据逆序,因为如果你顺序选择,有可能你选择两根筷子差的平方是最小了,但是却没有第三根比他们大的筷子来充当第三根筷子了
这个问题用DP来处理,状态转移方程为:dp(i,j)=min(dp(i,j-1),dp(i-1,j-2)+(a[j]-a[j-1])2);
i表示第几组,j表示在筷子中选择了第几根,dp(i,j)的含义为在第i组时,所有筷子中的第j根时,最小值为多少。
那么由此,我们可以知道,当处于第j根时有两种选择,
一种是作为整组筷子的第3根,那么d(i,j)明显等于d(i,j-1) (因为你都是第三根了,那你的值肯定是沿袭前一根的值)
一种是作为这一组最短的两根之一,那么当然dp(i,j)的答案就为:dp(i-1,j-2)+与前一根筷子差的平方。
然后对两种情况取min就是当前dp(i,j)的值了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<algorithm>
#include<bitset>
#define scand(a) scanf("%d",&a)
#define scandd(a,b) scanf("%d%d",&a,&b)
#define scanddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=5005;
int dp[maxn][maxn];
int main()
{
#ifdef local
freopen("1.txt","r",stdin);
#endif
int T;
cin>>T;
while(T--)
{
int chop[maxn];
int n,m;
scandd(n,m);
for(int i=1;i<=m;i++)scand(chop[i]);
sort(chop+1,chop+1+m,greater<int>());
mst(dp,0x3f);
for(int i=1;i<=m;i++)dp[0][i]=0;//易知不成一组时,值为0
for(int i=1;i<=n+8;i++)
{
for(int j=i*3;j<=m;j++)
{
int sum=(chop[j-1]-chop[j])*(chop[j-1]-chop[j]);
dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+sum);
}
}
printf("%d\n",dp[n+8][m]);
}
}