#598 (Div. 3) E.Yet Another Division Into Teams(dp)

题目描述

There are n students at your university. The programming skill of the i-th student is ai. As a coach, you want to divide them into teams to prepare them for the upcoming ICPC finals. Just imagine how good this university is if it has 2⋅105 students ready for the finals!
Each team should consist of at least three students. Each student should belong to exactly one team. The diversity of a team is the difference between the maximum programming skill of some student that belongs to this team and the minimum programming skill of some student that belongs to this team (in other words, if the team consists of k students with programming skills a[i1],a[i2],…,a[ik], then the diversity of this team is maxj=1ka[ij]−minj=1ka[ij]).
The total diversity is the sum of diversities of all teams formed.
Your task is to minimize the total diversity of the division of students and find the optimal way to divide the students.

Input

The first line of the input contains one integer n (3≤n≤2⋅105) — the number of students.
The second line of the input contains n integers a1,a2,…,an (1≤ai≤109), where ai is the programming skill of the i-th student.

Output

In the first line print two integers res and k — the minimum total diversity of the division of students and the number of teams in your division, correspondingly.
In the second line print n integers t1,t2,…,tn (1≤ti≤k), where ti is the number of team to which the i-th student belong.
If there are multiple answers, you can print any. Note that you don’t need to minimize the number of teams. Each team should consist of at least three students.

Examples

Input
5
1 1 3 4 2
Output
3 1
1 1 1 1 1
Input
6
1 5 12 13 2 15
Output
7 2
2 2 1 1 2 1
Input
10
1 2 5 129 185 581 1041 1909 1580 8150
Output
7486 3
3 3 3 2 2 2 2 1 1 1

Note

In the first example, there is only one team with skills [1,1,2,3,4] so the answer is 3. It can be shown that you cannot achieve a better answer.
In the second example, there are two teams with skills [1,2,5] and [12,13,15] so the answer is 4+3=7.
In the third example, there are three teams with skills [1,2,5], [129,185,581,1041] and [1580,1909,8150] so the answer is 4+912+6570=7486.

题目大意

有n个人参加比赛,每个人的能力是a[i]。你要给他们分组,每组不能少于三人。每组的差异性定义为:每组中能力最大的人的能力值减去每组中能力最小的人的能力值。总的差异值为每组差异值之和,问如何分配每组的人,才能使总的差异值最小。

题目分析

先给这n个数排序,这样就能保证每组的人都是连续的(注意要记得记录a[i]的下标)。

  1. 状态表示:f[i] //前i个人进行分组得到的最小差异值
  2. 状态计算:有两种情况
    1)将第i个人放在前一组中: 为了保证每组的差异值最小,所以肯定是把第i个人放在最后一组中,而最后一组的差异值为a[i-1]-a[这组的第一个数],因此我们只需要把a[i-1]改成a[i]就行了。f[i]=f[i-1]+a[i]-a[i-1]
    2)开一个新的组,放入第i个人: 这就需要从前面拿出两个人来和第i个人一起放进新组里。f[i]=f[i-3]+a[i]-a[i-2]

注意:因为最后要输出每个数的所在组的编号,因此我们要记录下每个组。用b[i]来记录每组的长度,b[i]表示以i结尾的i个数的最后一组的区间为[ b[i],i ]。 可以用它来还原每个数的所在组的编号数组。

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=2e5+5;
PII a[N];	//a[i].first记录原a[i],a[i].second记录原下标
int f[N],b[N],ans[N];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i].first);
		a[i].second=i;
	}
	sort(a+1,a+1+n);	//将a[i]从小到大进行排序
	int cnt=1,min=1e9;
	f[3]=a[3].first-a[1].first;
	for(int i=4;i<=n;i++)
	{
		if(i>=6&&min>f[i-3]-a[i-2].first)	//验证从i-2位置再分一个组能否得到更优的解
		{
			min=f[i-3]-a[i-2].first;
			cnt=i-2;		//记录这个组的开始位置
		}
		f[i]=f[i-1]+a[i].first-a[i-1].first;	//计算情况(1)
		b[i]=b[i-1];							//更新b[i]
		if(f[i]>min+a[i].first)		//计算情况(2)
		{
			f[i]=min+a[i].first;
			b[i]=cnt;		//记录这个新组的开始位置
		}
	}
	int pos=1,k=b[n];		//将b[i]转化为答案要求的数组ans
	for(int i=n;i>=1;i--)
	{
		ans[a[i].second]=pos;		//记录i位置所属的组
		if(i==k) pos++,k=b[i-1];	//如果一个组全部记录完了,则继续记录下一组
	}
	printf("%d %d\n",f[n],pos);		//输出答案
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值