题意:牛可乐有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;
}