题意:牛可乐有n个元素(编号1...n),第i个元素的能量值为ai。牛可乐可以选择至少k个元素来释放一次魔法,魔法消耗的魔力是这些元素能量值的极差.
形式化地,若所用元素编号集合为S,则消耗的魔力为这个集合中的最大值减最小值。
牛可乐要求每个元素必须被使用恰好一次。牛可乐想知道他最少需要多少魔力才能用完所有元素,请你告诉他。
(链接)[https://ac.nowcoder.com/acm/contest/3003/H]
分析:贪心地思考,要使每次使用的魔力尽量小,必须是一段连续元素的区间,即要从小到大排序,然后,我们再使用动态规划进行转移,f[i]表示用掉前i个元素的最小花费,当我们计算第i个元素的时候,
它肯定是作为最小元素被减去的,然后最大元素是[1,i - k + 1]之间的,我们表示为j,同时还要加上f[j - 1],即用掉前j - 1个元素的最小值,那么状态转移方程就是f[i] = min(f[i], f[j - 1] + a[j] - a[i]) j∈[1, i - k + 1]。
//对于时间复杂度为0(n^2)的DP问题,我们可以采用一些优化,可以利用之前前缀的最小值进行优化,这样,代码的时间复杂度就会降到o(n)。
时间复杂度o(n^2)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; using LL = long long; const int N = 3e5 + 7; int a[N]; //用掉前i个元素的最小代价 int f[N]; int main() { int n, k; scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } sort(a + 1, a + 1 + n); for (int i = 1; i <= n; ++i) f[i] = 2e9; for (int i = k; i <= n; ++i) { for (int j = 1; j <= i - k + 1; ++j) f[i] = min(f[i], f[j - 1] - a[j] + a[i]); } cout << f[n] << endl; return 0; }
时间复杂度o(n)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; using LL = long long; const int N = 3e5 + 7; int a[N]; //用掉前i个元素的最小代价 int f[N]; int pre; int main() { int n, k; scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } sort(a + 1, a + 1 + n); pre = -a[1]; for (int i = 1; i <= n; ++i) f[i] = 2e9; for (int i = k; i <= n; ++i) { f[i] = pre + a[i]; pre = min(pre, f[i - k + 1] - a[i - k + 2]); } cout << f[n] << endl; return 0; }
来源:https://www.cnblogs.com/pixel-Teee/p/12271966.html