传送门
题意:
n个数字,分成若干队伍(每个队伍至少3个数字),每个队伍都有最大最小值的差值,如何才能使所有队伍的最大最小差值之和最小。
题解:
容易有结论:人数 6,一定没有,人数6优
下证:不妨设
则任取
有
所以我们可以通过将所有队员划分为3、4、5人数的队伍,贪心得按照大小顺序选择队员一定优,然后通过dp划分即可得到最优解
至于举例即可从后往前倒退dp最优解得出,原因是,最优解确定即可从后往前判断是否从当前得到的最优解,即能推出最优划分情况
代码:
#include"algorithm"
#include"iostream"
#include"stdlib.h"
#include"string.h"
#include"stdio.h"
#define ll long long
using namespace std;
const int Maxn=200005;
struct node
{
ll sk,num;
}p[Maxn];
ll f1[Maxn];
int f2[Maxn],ans[Maxn];
inline bool cmp1(node a,node b)
{
return a.sk<b.sk;
}
int main()
{
// freopen("text.in","r",stdin);
memset(f1,999999,sizeof(f1));
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&p[i].sk),p[i].num=i;
sort(p+1,p+n+1,cmp1);
f1[3]=p[3].sk-p[1].sk;
f1[4]=p[4].sk-p[1].sk;
f1[5]=p[5].sk-p[1].sk;
f2[1]=f2[2]=f2[3]=f2[4]=f2[5]=1;
for(int i=6;i<=n;i++)
{
for(int j=2;j<=4;j++)
if(i-j-1>=3&&f1[i-j-1]+p[i].sk-p[i-j].sk<f1[i])
{
f1[i]=f1[i-j-1]+p[i].sk-p[i-j].sk;
f2[i]=f2[i-j-1]+1;
}
}
int cnt=0;
for(int i=n;i;)
{
if(f2[i]==1)ans[p[i].num]=cnt+1,i--;
for(int j=3;j<=min(5,i);j++)
if(f1[i]==f1[i-j]+p[i].sk-p[i-j+1].sk)
{
cnt++;
for(int k=j-1;k>=0;k--)
ans[p[i-k].num]=cnt;
i=i-j;
break;
}
}
printf("%lld %d\n",f1[n],f2[n]);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}