1.去掉前缀0和后缀1
2.将剩余的串划分成10串,每一位代表1或者0的个数
3.对于一个10节,b的值是一定的,假设1的数量为x,0的数量为y,那么可以求出一个函数f = x*(1-b)^2+y*b^2,化简后利用一元二次函数求最值的公式可知b取x/(x+y)时函数有最小值xy/(x+y)
4.用单调栈从左到右对每个10节进行遍历,每次比较当前10节的b值和栈顶10节的b值的大小,如果当前10节的b值大于栈顶10节的b值,那么直接将当前的10节入栈,否则弹栈直到当前10节的b值大于栈顶10节的b值,然后将当前的10节入栈。
5.将所有10节弹栈,每次根据公式xy/(x+y)累加结果
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <stack> #include <cmath> using namespace std; typedef pair<int, int> P; const int maxn = 1e5 + 10; const double eps = 1e-8; int dcmp(double x) { if (fabs(x) > eps) return 0; return fabs(x) > 0 ? 1 : -1; } int T, n, t[maxn], tot, num[maxn]; stack<P> s; int main() { scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &t[i]); tot = 0; while (!s.empty()) s.pop(); int l = 1, r = n; for (int i = 1; i <= n && t[i] == 0; i++) l++; for (int i = n; i >= 1 && t[i] == 1; i--) r--; if (l > r) { printf("%.6lf\n", 0.0); continue; } int cnt = 0; while (l <= r) { ++tot; cnt = 0; while (l <= r && t[l] == 1) l++, cnt++; num[tot] = cnt; ++tot; cnt = 0; while (l <= r && t[l] == 0) l++, cnt++; num[tot] = cnt; } s.push(make_pair(num[1], num[2])); for (int i = 3; i <= tot; i += 2) { int x = s.top().first, y = s.top().second; if (1.0*num[i]/(num[i]+num[i+1]) >= 1.0*x/(x+y)) s.push(make_pair(num[i], num[i+1])); else { int xx = num[i], yy = num[i+1]; while (1.0*xx/(xx+yy) < 1.0*x/(x+y)) { xx += x, yy += y; s.pop(); if (s.empty()) break; x = s.top().first, y = s.top().second; } s.push(make_pair(xx, yy)); } } double ans = 0; while (!s.empty()) { int x = s.top().first, y = s.top().second; s.pop(); ans += 1.0*x*y/(x+y); } printf("%.6lf\n", ans); } return 0; }