AcWing 549.训练 (前缀和)

AcWing 549训练
题目描述:
作为一名学校足球教练,你的任务是挑选一支由 P 个学生组成的团队代表你的学校。
共有 N名学生供你挑选,第 i 名学生的技术等级为 Si,这是一个正整数,表示他们的技术水平。
在你看来一个合理的团队中的 P个球员的技术应该是相当的,这样才能使每个人都融入到队内。
在最开始,你可能无法直接选出一个配置合理的队伍,因此你将为一些学生提供一对一的辅导。
将一名学生的技术等级提高 1需要你花费 1个小时的时间来进行辅导。
比赛季很快就开始了(事实上,第一场比赛已经开始了!),所以你想知道训练出一个合理的团队,你需要提供的最少训练小时数是多少。
输入格式:
第一行包含整数 T,表示共有 T 组测试数据。每组测试数据的第一行包含两个整数 N和 P。
第二行包含 N个整数 Si,其中第 i个整数为第 i个学生的技术等级。
示例:
3
4 3
3 1 9 100
6 2
5 5 1 2 3 4
5 5
7 7 1 7 7
输出样例:
Case #1: 14
Case #2: 0
Case #3: 6
算法思想:

本质上是求从n个数中挑选p个数使得其之间与最大者的差最小,所以首先对数组排序,问题转变为数组上p个连续数与p个数中最大值之间的差最小的值。

1、暴力枚举:

对数组遍历,每次枚举i和i + p-1之间的数与a[i + p - 1]之间差的和,变量minValue记录枚举过程中的最小值,数组遍历完毕后返回最小值即可。

c++代码实现:
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int t, n, p;
int main(){
    cin >> t; // 读入t组数据
    for(int k = 1; k <= t; k++){
        cin >> n >> p; // 每组数据读入n p;
        vector<int> a(n,0); // 创建数组a保存读入的n个数
        for(int i = 0; i < n; i++) cin >> a[i];
        sort(a.begin(),a.end()); // 对数组排序
        int minValue = 1e9 + 10,temp = 0; // minValue记录 i 枚举过程中p个数中与a[i + p -1]之间差的和最小值。
        for(int i = 0; i + p <= n; i++){
            for(int j = i; j < i + p ; j++){
               temp += (a[i + p - 1] - a[j]);
            }
            minValue = min(minValue,temp);// 更新minValue
            temp = 0;
        }
        printf("Case #%d: %d\n",k, minValue);
    }
    return 0;
}

时间复杂度 O(n^2)

2、前缀和

求p个单调递增的数即a[i + p -1]到a[i]之间与a[i]的差的和,可以转化为p * a[i] - (a[i-p + 1] + a[i-p + 2]+…+a[i])的值所以可以使用前缀和优化a[i-p + 1] 到 a[i]之间的数的和。

c++代码实现:
#include <iostream>
#include <algorithm>
#include <limits.h>

using namespace std;
const int N = 1e5 + 10;
int a[N],sum[N]; // 定义a数组保存每次读入的n个数,sum数组记录a数组的前缀和

int main(){
    int t;
    cin >> t;
    for(int k = 1; k <= t; k ++){
        int n, p;
        cin >> n >> p;
        for(int i = 1; i <= n; i++) cin >> a[i]; // 读入n个数
        sort(a + 1,a + 1 + n); // 对 a 数组排序
        for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i]; // 构造a的前缀和数组
        
        int ans = INT_MAX;
        for(int i = p; i <= n; i++){
            ans = min(ans,p * a[i] - (sum[i] - sum[i-p])); // 转化为p个a[i]减去a[i-p+1]到a[i]的和
        }
        printf("Case #%d: %d\n", k,ans);
    }
    return 0;
}
时间复杂度:

对数组a排序的时间复杂度是 O(nlogn)。其余都是O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值