有 n n n根长度不相同的筷子,现在有 K K K个人需要使用筷子,每个人需要三根筷子,一个人的贡献为短的两根筷子的差值的平方,即 ( A − B ) 2 (A-B)^2 (A−B)2。求 K K K个人选完筷子的最小贡献和。
如果每个人只选择两根筷子的话,题目就简单了不少。因为肯定会选择连续的两根筷子,也就是 n n n个排好序的数,选出 k k k对数贡献最小。那么就有
令 D P [ i ] [ 1 / 0 ] [ j ] DP[i][1/0][j] DP[i][1/0][j]为第 i i i个数,选、不选时,总共有 k k k对数的最小贡献。
则有状态转移方程:
//要选i-1和i这一对数 那么前一个数肯定不能选
DP[i][1][j] = min(DP[i][0][j], DP[i - 1][0][j]);
//不选i-1和i这一对数 那么前一个数可选可不选
DP[i][0][j] = min(DP[i][0][j], DP[i - 1][0][j]);
DP[i][0][j] = min(DP[i][0][j], DP[i - 1][1][j]);
题目要求每个人三根筷子,那么从大到小选,并且加上限制条件 j ∗ 3 < = i j*3<=i j∗3<=i 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 5005, M = 1010;
int a[5010];
long long dp[N][2][M]; //第i根筷子 选/不选 凑出j双的方案数
void solve()
{
memset(dp, 0x3f3f3f3f, sizeof dp);
int k, n;
scanf("%d %d", &k, &n);
k += 8;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n, greater<int>());
for (int i = 0; i <= 2; i++)
dp[i][0][0] = 0;
for (int i = 3; i <= n; i++)
{
for (int j = 0; j <= min(i / 3, k); j++)
{
if (j >= 1)
dp[i][1][j] = min(dp[i][1][j], dp[i - 1][0][j - 1] + (a[i - 1] - a[i]) * (a[i - 1] - a[i]));
dp[i][0][j] = min(dp[i][0][j], dp[i - 1][1][j]);
dp[i][0][j] = min(dp[i][0][j], dp[i - 1][0][j]);
}
}
cout << min(dp[n][0][k], dp[n][1][k]) << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}