为了更好的阅读体检,可以查看我的算法学习网
在线评测链接:P1070
题目内容
塔子哥是一名数学家,他一直在研究一个关于序列划分的数学问题。他有一个长度为 n n n 的数组,数组中的每个元素都是一个非负整数。他想将这个数组划分成 k k k 个子序列,使得每个子序列中的元素恰好出现一次,且每个子序列都必须非空。他希望将这 k k k 个子序列的平均数之和尽可能地小,这样他就能够更深入地研究这个问题。
为了解决这个问题,塔子哥来到了你的面前,请求你帮助他找到一种最小化平均数之和的划分方案,使得这 k k k 个子序列的平均数之和尽可能小。你能帮帮他吗?
注,子序列可以不连续。例如数组为 [ 3 , 2 , 1 , 3 ] [3,2,1,3] [3,2,1,3] , k = 2 k=2 k=2 时,子序列可以拆分为 [ 3 , 1 ] [3,1] [3,1] 和 [ 2 , 3 ] [2,3] [2,3]
输入描述
第一行输入两个正整数 n n n 和 k k k ,代表数组的长度、子序列的数量。
第二行输入 n n n 个正整数 a i a_i ai ,代表数组的元素。 保证序列不含重复元素
1 ≤ k ≤ n ≤ 1 0 5 1\le k\le n\le 10^5 1≤k≤n≤105
− 1 0 9 ≤ a i ≤ 1 0 9 -10^9\le a_i \le 10^9 −109≤ai≤109
输出描述
一个小数,代表最终平均数之和的最小值。保留5位小数
样例
输入
7 3
9 4 5 6 2 1 7
输出
9.20000
k序列平均值和最小
思路:
假设 k = 1 k=1 k=1时,只能分为 1 1 1个。
k = 2 k=2 k=2时,我们要需要分为两个数组,假设原数组为arr,分开后的数组为 a r r 1 arr1 arr1, a r r 2 arr2 arr2,那么我们讨论 a r r 1 arr1 arr1存有几个数最好。
其实只有一个数是最好的,且这个数最小才是最好的,即 a r r 1 arr1 arr1只要一个最小数,其他 n − 1 n-1 n−1个数在 a r r 2 arr2 arr2.
证明:
我们对 a r r arr arr进行排序,那么假设 a r r 1 = a r r [ 0 ] arr1=arr[0] arr1=arr[0],所以平均值为和 a r r [ 0 ] + ( s u m − a r r [ 0 ] ) / ( n − 1 ) arr[0]+(sum-arr[0])/(n-1) arr[0]+(sum−arr[0])/(n−1),此时我们需要证明为何需要一个数,多个数可以吗
我们假设存在一个数 a r r [ i ] arr[i] arr[i],其中 i > 0 i>0 i>0,让这个 a r r [ i ] arr[i] arr[i]并入 a r r 1 arr1 arr1数组,那么平均值和为 ( a r r [ 0 ] + a r r [ i ] ) / 2 + ( s u m − a r r [ 0 ] − a r r [ i ] ) / ( n − 2 ) (arr[0]+arr[i])/2+(sum-arr[0]-arr[i])/(n-2) (arr[0]+arr[i])/2+(sum−arr[0]−arr[i])/(n−2),我们发现平均值由左右两部分组成,左边部分增加了很多,而右边部分减少了一点,所以平均值会增加。当然这只是感性的证明,如果要严格点,需要将平均值的差值进行求解然后证明。
如果只能分一个数字,那么肯定是最小的,这个证明比较简单,假设存在一个树 a r r [ i ] arr[i] arr[i],那么平均值和为 a r r [ i ] + ( s u m − a r r [ i ] ) / ( n − 1 ) arr[i]+(sum-arr[i])/(n-1) arr[i]+(sum−arr[i])/(n−1),和原始的平均值做个差值即可证明。
Java代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = sc.nextInt();
Arrays.sort(a);
double ans = 0;
double sum = 0;
for (int i = 0; i < k - 1; i++)
ans += a[i];
for (int i = k - 1; i < n; i++)
sum += a[i];
ans = ans + sum / (n - k + 1);
System.out.println(ans);
}
}
C++代码
#include <iostream>
#include <algorithm>
using namespace std;
int a[100010];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin>>a[i];
sort(a,a+n);
double ans = 0;
double sum = 0;
for (int i = 0; i < k - 1; i++)
ans += a[i];
for (int i = k - 1; i < n; i++)
sum += a[i];
ans = ans + sum / (n - k + 1);
cout<<ans<<endl;
return 0;
}
python
line = list(map(lambda x: int(x), input().split()))
n,k = line[0],line[1]
numb = list(map(lambda x: int(x), input().split()))
numb.sort()
sum = 0.0
tt = 0
for i in range(k-1):
sum += float(numb[i])
for i in range(n-k+1):
tt += numb[k-1+i]
sum += float(tt/(n-k+1))
print("%.5f" % sum)
js
let line = readline().split(" ").map(Number);
let n = line[0], k = line[1];
let numb = readline().split(" ").map(Number);
numb.sort((a, b) => a - b);
let sum = 0.0;
let tt = 0;
for (let i = 0; i < k - 1; i++) {
sum += numb[i];
}
for (let i = 0; i <= n - k; i++) {
tt += numb[k - 1 + i];
}
sum += tt / (n - k + 1);
console.log(sum.toFixed(5));
题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。