题目描述:
给定一个长度为n的非负整数序列A,求一个平均数最大的,长度不小于L的子段 。
输入描述:
第一行用空格分隔的两个整数n和L;
第二行为n个用空格隔开的非负整数,表示Ai。
输出描述:
输出一个整数,表示答案的1000倍。不用四舍五入,直接输出。
二分查找:
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
思路:
可以先用二分查找到一个平均值avg来进行判定,能否成立一个子段大于等于F且大于等于这个平均值avg。
对于一段序列,每个数减去我们所算的平均数,如果大于0 那么他本身就大于平均数,如果小于0 那么它本身就小于平均数。
之后可以利用前缀和:s[i] = s[i - 1] + num[i] - avg。(关于这个式子怎么推出来的,我也表达不清楚(哭笑.jpg))如果这个前缀和中存在长度大于等于F且和大于等于0,就成立。
完整C++版AC代码:
#include <cstdio> #include <iostream> const int N = 100005; int cows[N]; double sum[N]; int n, m; bool check(double avg) { for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + cows[i] - avg; } double minv = 0; for (int i = 0, j = m; j <= n; j++, i++) { minv = std::min(minv, sum[i]); if(sum[j] - minv >= 0) return true; } return false; } int main() { scanf("%d %d", &n, &m); double l = 0, r = 0; for (int i = 1; i <= n; i++) { scanf("%d", &cows[i]); r = std::max(r, (double)cows[i]); } while(r - l > 1e-5) { double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid; } printf("%d\n", (int)(r * 1000)); return 0; }