说实话挺喜欢soj的界面,简简单单,没有多余的东西hhh(但是简单到连内存限制,时间限制都看不到了。
题意是有个“奇葩”的主人公,吃饭要用三根筷子。两根短的一根长的。
现在给你n根筷子,要在里面挑k+8对筷子(一对三根,有一根最长的,设为Ai <= Bi <= Ci (Ai-Bi)^ 2 叫做 badness)使得
$\sum ^{k}_{i=1}\left( A_{i}-B_{i}\right) ^{2}$ 最小
emmmmm其实不放在dp分类里面我看不出是dp,以为是贪心(太菜了
dp[i][j]表示前i根筷子里面取j对的badness的最小值
正着取怎么取怎么不舒服,因为不知道以谁结尾,第i-3根在之前取过吗 第i-2根在之前取过吗 第i-1根在之前取过吗 有最大的跟这些匹配吗,
所以就由大到小取,只要 3 * j <= i,就有大的跟这一组对应。
转移方程dp[i][j] = min(dp[i-1][j], dp[i-2][j-1] + (l[i] - l[i-1])^2)
不过会卡内存,dp数组得设得刚刚好,不然就用滚动数组(菜到不会
时间复杂度O(kn)
代码如下
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; const int maxn = + ;
int n, k, l[maxn], dp[maxn][maxn/]; int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &k ,&n);
k += ;
for (int i = ; i <= n; i++) {
dp[i][] = ;
scanf("%d", &l[n-i+]);
}
for (int i = ; i <= n; i++) {
dp[i][] = ;
for (int j = ; j <= k; j++)
dp[i][j] = 0x3f3f3f3f;
}
for (int i = ; i <= n; i++) {
for (int j = ; j <= min(k, i / ); j++) {
dp[i][j] = min(dp[i-][j], dp[i-][j-] + (l[i] - l[i-]) * (l[i] - l[i-]));
}
}
printf("%d\n", dp[n][k]);
}
return ;
}