题意:有N支筷子,要求其中选出M套筷子,每一套筷子有A,B,C三支,并满足A<=B<=C,并称 (A−B)2 为每套筷子的’badness’,现在要求这M套筷子的’badness’总和最少,求最少的总和是多少.
分析:现在我们先不考虑C,我们只考虑每套筷子只有两支,并使得’badness’总和最少应该满足什么条件, 现在我们假设有a1,a2,a3,a4四支筷子,并且a1<=a2<=a3<=a4,应该如何配套才能使’badness’最小呢.可以看出这里一共有A{{a1, a2}, {a3,a4}} , B{{a1, a3}, {a2, a4}} , C{{a1, a4}, {a2, a3}}三种配套方式,很明显badness(B) >= badness(A), 我们比较 badness(A)和badness(C)的大小关系, badness(A) - badness(C) = 2(a4-a2)(a1-a3) <= 0所以badness(A) <= badness(C). 所以可以推论只有所有配套的筷子都是相邻的两个才能使得总得badness最小. 此时我们便可以考虑背包来做,定义dp[i][j]表示前i个筷子,组成j双筷子的时候最小的badness值,则dp转移方程为dp[i][j] = min(dp[i-1][j], dp[i-2][j-1] + (a[i] - a[j])^2).
但是这道题每套筷子不止一双,还需要有一支C,怎么处理呢? 其实有一个很巧妙的办法,把C留出来就好了,可以这样处理,把筷子的长度从大到小排列
for(int i = 1; i <= K; i++) {
for(int j = 3*i; j <= N; j++) {
dp[j][i] = min(dp[j-1][i], dp[j-2][i-1] + (A[j] - A[j-1])*(A[j] - A[j-1]));
}
}
外层需要表示第i套筷子,很明显要保证有足够的C留着配套,需要从 最后一双筷子必须要从3*i开始选,不然的话一定有双筷子A,B没有配套的C. 这样转移的话就可以保证了每双筷子都有了配套,很巧妙的处理方式!!
代码:
#include
using namespace std;
typedef long long LL;
const int maxn = 5000 + 5;
const int maxk = 1000 + 10;
const int inf = 1<<30;
int A[maxn];
int dp[maxn][maxk];
int K, N;
int main()
{
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &K, &N);
K += 8;
for(int i = N; i > 0; i--) {
scanf("%d", &A[i]);
}
for(int i = 0; i <= N; i++) {
dp[i][0] = 0;
for(int j = 1; j <= K; j++) {
dp[i][j] = inf;
}
}
for(int i = 1; i <= K; i++) {
for(int j = 3*i; j <= N; j++) {
dp[j][i] = min(dp[j-1][i], dp[j-2][i-1] + (A[j] - A[j-1])*(A[j] - A[j-1]));
}
}
printf("%d\n", dp[N][K]);
}
return 0;
}