题意:这里有n个同学在你的大学,第i个同学的编程技巧是ai,作为一个教练,你想把它们分成很多组。每组至少3个人。
求每组差异的值的总和。如何分组,才能使得差异值的总和最小。

分析:先排序,肯定是连续数字的组中最大值和最小值差值最小。然后考虑分组的人数的上限,我们最多一组5个人,
因为假设有6个人,差值为a6 - a1,我们可以分成更多的组数,使得差值减小,比如a3 - a1和a6 - a4,这两组的差值加起来
等于a6 - a1 + a3 - a4,a3 - a4 < 0,因此答案会变小。

递推方程为 f[i + j] = min(f[i + j], f[i] + a[i + j].val - a[i + 1].val);

然后这道题要求我们输出具体的分组方案,求具体的分组方案,我们可以记录每个状态从哪个状态转移过来,然后从最后一个状态反推。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
using LL = long long;
const int N = 200005;
//LL a[N];
LL f[N];//处理前i个人,得到的最小差值
int p[N];//组编号
int res[N];
struct Node
{
	LL val;
	int id;//编号
	bool operator<(const Node& rhs) const
	{
		if (val == rhs.val)
			return id < rhs.id;
		return val < rhs.val;
	}
}a[N];

int main()
{
	memset(f, 0x3f, sizeof f);

	int n;
	scanf("%d", &n);

	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i].val);
		a[i].id = i;//编号
	}

	sort(a + 1, a + n + 1);

	f[0] = 0;
	for (int i = 0; i <= n; ++i)
	{
		for (int j = 3; j <= 5 && i + j <= n; ++j)
		{
			LL diff = a[i + j].val - a[i + 1].val;
			if (i + j <= n && f[i + j] > f[i] + diff)
			{
				f[i + j] = f[i] + diff;
				p[i + j] = i;//从i转移过来
			}
		}
	}

	int cur = n;
	int cnt = 0;//组数

	while (cur != 0)
	{
		for (int i = cur; i >= p[cur] + 1; --i)
		{
			res[a[i].id] = cnt;
		}
		++cnt;
		cur = p[cur];
	}

	cout << f[n] << " " << cnt << endl;

	for (int i = 1; i <= n; ++i)
		printf("%d ", res[i] + 1);

	return 0;
}