题目
有nnn根长度不相同的筷子,现在有KKK个人需要使用筷子,每个人需要三根筷子,一个人的贡献为短的两根筷子的差值的平方,即(A−B)2(A-B)^2(A−B)2。求KKK个人选完筷子的最小贡献和。
解题思路
如果每个人只选择两根筷子的话,题目就简单了不少。因为肯定会选择连续的两根筷子,也就是nnn个排好序的数,选出kkk对数贡献最小。那么就有
令DP[i][1/0][j]DP[i][1/0][j]DP[i][1/0][j]为第iii个数,选、不选时,总共有kkk对数的最小贡献。
则有状态转移方程:
//要选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<=ij*3<=ij∗3<=i 即可。
AC代码
#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;
}