正在网易云课堂学习王宏志老师的算法设计与分析入门篇课程视频,将学习中的作业问题发上来与大家一同讨论。这篇是对第五周的作业第二题个人的一些思路,希望与大家一同学习。
夺冠热门
题目内容:
n 个人将会进行一轮比赛,决出 1 到 n 名。(没有并列)
这轮比赛开始之前,每个人已经有一个初始分数(可能有并列)。在这轮比赛结束后,第 i 名的人将获得 n-i+1 分。
问这轮比赛结束后,有望得到第一的人会有多少人(并列第一也算第一)。
输入格式:
第一行一个整数 n(0 <= n <= 1000),表示人数。
接下来的一行,每行一个正整数 ai(0 <= ai <= 1000),表示每个人的初始分数。
输出格式:
输出一行一个整数 n,表示可能夺冠的人数。
输入样例:
5
3 4 5 6 9
输出样例:
3
Hint:
9 分的那个人至少也会得到 1 分因而是 10 分。
而 5 + 5 = 10,因此 5 分的人也是有望夺冠的。
再低就不行了。
时间限制:2000ms内存限制:128000kb
#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>
#include <functional>
using namespace std;
float max(vector<float> p){
float max = p[0] + 1;
for (int i = 1; i < p.size(); i++){
p[i] = p[i] + i + 1;
if (max < p[i])
max = p[i];
}
return max;
}
int main(){
int n;
while (cin >> n){
if (n == 0){
cout << 0 << "\r\n";
system("pause");
}
vector<float> p(n);
for (int i = 0; i < n; i++){
cin >> p[i];
}
sort(p.begin(), p.end(), greater<float>());
//可能得第一的选手初始的最低成绩
float zuidi = max(p) - n;
//可能得第一的人数
int mayfirst = 1;
while (mayfirst < n && p[mayfirst] >= zuidi){
mayfirst++;
}
cout << mayfirst << "\r\n";
}
system("pause");
}
个人认为没有问题,但就过了一个测试用例。
正确性证明:
n个人按分数从大到小排列
k1 k2 k3 …… kn,分别加上1 2 3 ……. n
得到sum1 sum2 sum3 ………sumn,求出最大值为summ,设min=summ-n。
//min为可能的第一的最低基础分,下面会证明。
因为summ = km+m >=kn+n,所以min =summ-n>=0.
则对于ki>=min就有,
k1 k2 k3 ………ki……. kn ,仅将kn与ki的第二轮得分互换即ki+n,kn+i。
由于ki>=min,所以ki+n>=min+n=summ,又因为summ为原得分情况sum1 sum2 sum3 ………sumn中最大值,而更改得分的仅为ki与kn,所以summ依旧为更改后分情况sum1 sum2 sum3 ………sumn中除sumi外最大值(sumn变小,不影响)。
则sumi>=任意选手的总分,则第i名选手至少为并列第一。
对于ki < min的有,
k1 k2 k3 ………ki……. kn ,将kn与ki的第二轮得分互换后,sumi依旧小于summ。而如果想要将summ变小,则km要加上一个小于m的数设为m1。但对于原来加上m1的数km1而言km1>=km,交换后km1+m>=km+m>ki+n。sumi依旧不是最大的,并永远没可能是最大的。
所以可能为冠军的选手的初始分数一定大于等于min。
并且对于任何初试分数大于等于min的选手就有可能得冠军。