Kickstart 2019 RoundA 1. Training

题目描述

这是Kickstart 2019 RoundA 的第一道题目,题目原文描述如下:

As the football coach at your local school, you have been tasked with picking a team of exactly P students to represent your school. There are N students for you to pick from. The i-th student has a skill rating Si, which is a positive integer indicating how skilled they are.

You have decided that a team is fair if it has exactly P students on it and they all have the same skill rating. That way, everyone plays as a team. Initially, it might not be possible to pick a fair team, so you will give some of the students one-on-one coaching. It takes one hour of coaching to increase the skill rating of any student by 1.

The competition season is starting very soon (in fact, the first match has already started!), so you’d like to find the minimum number of hours of coaching you need to give before you are able to pick a fair team.

题目解读

有N个足球队员,每个队员的水平为Si。教练要做的是,从这N个人中挑选出P个成员,这P个成员的水平都是一样的。由于每个队员的水平可能不一样,假设这P个成员中水平最高的为Smax,教练可以对水平低于这个最高水平的队员进行训练,教练每次只能一对一训练,花费1小时将一个队员的水平提高1个单位,直到所有队员水平都达到Smax。求教练如何选这P个队员,才能将教练训练的时间压缩到最短?并返回最短时间。

输入

The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with a line containing the two integers N and P, the number of students and the number of students you need to pick, respectively. Then, another line follows containing N integers Si; the i-th of these is the skill of the i-th student.

Time limit: 15 seconds per test set.
Memory limit: 1 GB.
1 ≤ T ≤ 100.(T表示测试用例的数目)
1 ≤ Si ≤ 10000, for all i.
2 ≤ P ≤ N.

Test set 1 (Visible)
2 ≤ N ≤ 1000.

Test set 2 (Hidden)
2 ≤ N ≤ 105.

测试样例:
Input

3
4 3
3 1 9 100
6 2
5 5 1 2 3 4
5 5
7 7 1 7 7

Output

Case #1: 14
Case #2: 0
Case #3: 6

解题思路

第一种思路:超时

我们可以这么想,首先将数组从小到大进行排序,然后从尾巴开始遍历,例如上面的第一个test case,N= 4, P=3,Si = {3,1,9,100}。排序后,Si={1,3,9,100}。首先从最后一个元素100开始遍历,如果我选择的3个队员中,100是队员的最高水平,那么我要再选另外2个队员时,肯定要选择水平上界为100的队员中,水平最高的,这样才能使得训练时间最短。也就是说我要选择3和9,这时训练时间为188。第二步遍历到9,也就是说,如果我选择的3个队员中,9是队员的最高水平,那么我也要选择水平上界为9的队员中水平最高的两个,这样才能使得训练时间最短,也就是说我要选择1和3,这时训练时间为14。由于再往前遍历无法组成P=3的队伍,所以14就是最小的训练时间。这种方法的时间复杂度为O(NP),因为我最多可能有N次遍历,每次遍历的时间复杂度为O(P),因此最终的时间复杂度为O(NP)。这种方法是超时的,sad。

数组索引0123
数组元素139100
最短训练时间NANA14188

第二种思路:AC

这种思路主要参考了这篇文章,通过计算数组的前缀和,将时间复杂度降低到了O(N)。首先我们还是将数组从小到大排序,用pre保存当前元素的前缀和。然后从0位置开始遍历数组,如果当前位置i<P-1,说明当前的元素还无法构成一个队伍,将pre加上当前元素的值,表示当前i+1个元素的和。如果i==P+1,那么说明当前元素可以构成一个队伍,pre加上当前元素的值,表示当前P个元素的和,这是用P个元素中水平最大的为nums[i],也就是当前元素的值,如果想要把之前的队员都训练为nums[i],那么最后P个队员的水平之和应该为Pnums[i],但是目前P个队员的水平之和为pre,所以需要的时间为Pnums[i]-pre。之后继续遍历下一个元素,pre表示到当前为止的P个队员的水平之和,所以现在的pre不仅要加上当前队员的水平,还要减去最开始的一个元素的值,例如Si = {1,3,9,100},遍历到100的时候,pre此时的值为1,3,9的和,但是pre现在要表示为到元素100为止的3个元素的和,所以pre要加上100,再减去P位之前,也就是元素1的值,此时pre为112。这样可以同理计算出最短训练时间。图解如下:

数组索引0123
数组元素139100
前缀和1313112
最短训练时间NANA9*3-13=13100*3-112=188

AC啦

AC代码:

#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
class Solution{
public:
	int minTime(vector<int> nums, int P){
		sort(nums.begin(), nums.end());
		int pre = 0;
		int re = 10000000000;
		for (int i = 0; i < nums.size(); i++){
			if (i < P-1){
				pre+=nums[i];
			}
			else if (i == P - 1){
				pre += nums[i];
				re = min(re, P*nums[i] - pre);
			}
			else{
				pre += nums[i] - nums[i-P];
				re = min(re, P*nums[i] - pre);
			}
		}
		return re;
	}
};
int main(){
	int T;
	int N;
	int P;
	cin >> T;
	int i = 1;
	while (T > 0){
		cin >> N;
		cin >> P;
		vector<int> nums(N, 0);
		for (int i = 0; i < N; i++){
			cin >> nums[i];
		}
		T--;
		Solution s;
		int re = s.minTime(nums, P);
		cout << "Case #"<<i<<": " << re<<endl;
		i++;
	}
	return 0;
}

肥肠开心啦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值