CF598 div3 E. Yet Another Division Into Teams(贪心+DP)

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43329358/article/details/103024088