KS 2018 Round A:第二题
这一题我快做出阴影了,当然题目不好做,我看了别人的讲解才做出来,但是自己实现的时候各种狗屁错误,我tm。。。
本题做法:
E(k):表示可以redip k次的最优期望。
E(0) = sum(Vi) / N
E(k): 如果取出的v > E(k-1),那么留下;否则redip。
转移方程为:E(k) = (smallerCount * E(k-1) + largerSum) / N;这里的small large是相对于dp[k-1]说的。
所以这里要使用二分查找,而且的话使用前缀和来很快地知道大于部分的和。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;
double helper(vector<int>& nums, int K);
int main() {
int T;
cin >> T;
int cnt = 0;
while (++cnt <= T) {
int N, K;
cin >> N >> K;
vector<int> nums;
int num;
while (N-- > 0) {
cin >> num;
nums.push_back(num);
}
double res = helper(nums, K);
//cout << "Case #" << cnt << ": " << res << endl;
printf("Case #%d: %f\n", cnt, res);
}
}
double helper(vector<int>& nums, int K) {
int sz = nums.size();
vector<double> dp(K+1, 0.0);//注意是K的大小
sort(nums.begin(), nums.end());
vector<long long> sums(sz, 0);
sums[sz-1] = nums[sz-1];
for (int i = sz-2; i >= 0; --i)
sums[i] = sums[i+1] + nums[i];
dp[0] = ((double)sums[0]) / sz;
for (int i = 1; i <= K; ++i) {
auto iter = upper_bound(nums.begin(), nums.end(), dp[i-1]);
int cnt = iter - nums.begin();
dp[i] = (cnt*dp[i-1] + (cnt == sz ? 0 : sums[cnt])) / sz;
}//注意cnt超出范围,在尾后的位置的情况
return dp.back();
}
解法简单明了,但是自己实现的时候各种错误(比如dp的大小一开始设置为了sz等等),需要之后一直注意的几个问题有:
1.可以使用upper_bound, lower_bound, 以及binary search 来进行二分查找,含义和map的函数是一样的(https://www.cnblogs.com/Tang-tangt/p/9291018.html)。但是需要注意的一点是:upper_bound, lower_bound,它们返回的位置可能是尾后的位置,所以数组可能会越界。我一开始就是犯了这个错误,导致卡住,而且这种越界的错误最难排查,累死我了。
2.如果容器底层是连续内存的话,可以使用迭代器加减。
3.另外,对于kickstart,如果输出的是小数,那么就得用printf(#include < cstdio >)。可以看我注释掉的那一行,用cout输出就是错的,用printf就是对的,即使没有保留几位小数,但还是有用。
今天一晚上做了一题,我真的是fuck了。但是也收获不少吧,更多的是感觉ks好像没我想象的那么难,确实比平时lc做的要难,但是也有意思很多,也不是自己想不出来的那种,无非是:1.这种题目不熟悉2.这里码代码和lc上不太一样,不太适应。自己熟悉和适应之后是可以独立完成的ks的。