CF598 div3 E. Yet Another Division Into Teams
题目意思:学校有n个人,然后让他们组队,至少三个人才能组成一个队伍。每个人有一个值ai代表能力,一个队伍的d值就是队员能力最大值减去最小值。问你怎么安排队伍,使得所有队伍的d值相加最小。
解题思路:首先要差值最小,所以肯定要把能力相近的人放一起。所以一开始的处理用了stl里的pair,pair自带两个值,可以分别定义类型。如果题目有需要记录下标啥的话,用pair就很方便不用自定义结构体了。存完数据以后调用一下sort,pair的sort是这样的:若不带cmp就是默认按照pair.first排序如果first相同就考虑second。然后更改排序的方式的话,重写cmp就是了。
排完序使得人都是从小到大排了以后开始分组。然后就用到了贪心的想法,首先5个人只能排一组,如果有六个人的话,就一定分成两组。因为两组的差值一定小于等于一组的差值。这个很好证明。
最后的实现就是简单的动态规划,dp[i]为排到第i个人的时候整个的差值的话,dp[i]=min(dp[i-step-1]+a[i].first-a[i-step].first,dp[i]).step从2到4就代表和前面几个人组成一队。但是要先判断前i-step-1个人是否组成了队伍。
用一个pre数组维护前一组的最后一个成员的标号,然后dfs就能给他们排好队伍了。最后sort一下得到按序的排放。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int SIZE=2*100000+10;
int n;
int group;
int dp[SIZE];
int pre[SIZE];
pair <int,int> a[SIZE];
void dfs(int x){
if(x==0) return;
group++;
for(int i=x;i>pre[x];--i) a[i].first=group;
dfs(pre[x]);
}
bool cmp(pair<int,int> a,pair<int,int> b){
return a.second<b.second;
}
int main()
{
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i].first;
a[i].second=i;
}
sort(a,a+n+1);
memset(dp,-1,sizeof(dp));
dp[0]=0;
dp[3]=a[3].first-a[1].first;
for(int i=4;i<=n;++i){
for(int step=2;step<5;++step){
if(dp[i-step-1]!=-1)
if(dp[i]==-1||dp[i]>=dp[i-step-1]+a[i].first-a[i-step].first){
dp[i]=dp[i-step-1]+a[i].first-a[i-step].first;
pre[i]=i-step-1;
}
}
}
group=0;
dfs(n);
printf("%d %d\n",dp[n],group);
sort(a,a+n+1,cmp);
for(int i=1;i<=n;++i){
printf("%d ",a[i].first);
}
return 0;
}