CF1256E Yet Another Division Into Teams rating2000

博客围绕将n个学生分成k个队伍,使所有队伍极差总和最小的问题展开。先对学生水平排序,因队伍人数要求,分析不同人数情况。采用动态规划,定义dp[i]为前i个人分队时的最小极差和,得出转移方程,最后用双指针确定具体方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

有n个学生,学生的算法水平为a1,a2,a3…,an,现在把他们分成k个队伍,每个队伍至少有3个人,定义极差为每个队伍中最高水平和最低水平之差,求如何分队才能使所有队伍极差总和最小。

输入:

学生人数n(3<=n<=2e5)

学生水平a1,a2,a3…,an(1<=ai<=1e9)

输出:

最小极差总和res,最优队伍数k

t1,t2,t3…,tn,ti表示第i个学生的队伍编号

Solution:

为了便于求最小极差和,我们先将ai进行有小到大排序。

由于题目要求,队伍人数不能小于3个人,故当n为1~5时,队伍数都为1。而当n=6时,我们有两种选择,一种是只分一队,第二种是分两支3人队,而当我们讲这两种情况的极差和进行对于,由于ai递增,我们会发现a6-a4+a3-a1>=a6-a4+a4-a1=a6-a1,显然分成两队所得的极差和一定不会比一队的差。

由此我们可以推出当一支队伍能分至不可分时才能得到最小极差和,但是由于一个队伍可以有3,4,5人3种人数(例如:n=9时,我们并不知道分成3队人数分别为3,3,3更优,或者是2队人数分别为4,5更优),故我们采用dp的方式来得到最终答案。

对于dp[i]的状态表示,我们定义为前i个人分队时的最小极差和。

由以上分析,我们只要去枚举对于当前第i个人组成的队伍人数为3,4,5人

便能得到转移方程为dp[i]=min(dp[i],dp[j]+a[i]-a[j+1]),i-5<=j<=i-3。

对于最后的具体方案,我们只要简单的双指针去判断dp[n]由哪个状态转移而来的便能得到最终答案。

C++code:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n;
int t[N], dp[N];
struct mys {
    int a, p;
    bool operator<(const mys& W)const {
        return a < W.a;
    }
}m[N];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(dp, 0x3f, sizeof dp);//初始化dp数组
    dp[0] = 0;

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> m[i].a;
        m[i].p = i;
    }
    sort(m + 1, m + 1 + n);

    for (int i = 3; i <= n; i++) {//dp转移过程
        for (int j = max(0, i - 5); j <= max(0, i - 3); j++) {
            dp[i] = min(dp[i], dp[j] + m[i].a - m[j + 1].a);
        }
    }

    int i = n, j = n - 3, team = 1;//求总队数以及每个人对应的队伍编号
    while (j >= 0) {
        if (dp[i] == dp[j] + m[i].a - m[j + 1].a) {
            for (int k = j + 1; k <= i; k++)t[m[k].p] = team;
            i = j, j = i - 3;
            team++;
        }
        else j--;
    }

    cout << dp[n] << ' ' << team - 1 << endl;
    for (int i = 1; i <= n; i++)cout << t[i] << ' ';
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值