首页 > 代码库 > hdu 4923 Room and Moor (单调栈+思维)
hdu 4923 Room and Moor (单调栈+思维)
题意:
给一个0和1组成的序列a,要构造一个同样长度的序列b。b要满足非严格单调,且
值为0到1的实数。最后使得 sum((ai-bi)^2)最小。
算法:
首先a序列开始的连续0和末尾的连续1是可以不考虑的。因为只要b序列对应开头为0、
末尾为1,既不影响单调性又能使对应的(ai-bi)^2=0。
然后,
先找111100、11100、10这样以1开始以0结束的序列块。每一块对应的b值相等且均为
这一块的平均值,即1的个数/0和1的总个数。
但是要满足b的单调性,则我们用栈来维护,如果后面一块的均值<前面一块的均值,则
合并这两块。也就是只要栈顶的块的均值小于要压入栈的块的均值,就一直合并,直到
满足单调性,再把当前的值压入栈。
最后只要一块块统计对应的sum((ai-bi)^2)就是答案了。
逗逼记忆---->比赛的时候我没有想到块,只想到除去前面的0和后面的1,中间的值都是一样,
用二分或者三分解决,只要控制精度=。=
P.S: 这题必须注意细节,否则极易丢失精度,主要是我代码注释的两个我开始没有控制好的地方。
o(╯□╰)o
#include<cstdio> #include<iostream> #include<cstring> #define maxn 100010 using namespace std; struct node { double num,v; //v表示1的个数,num表示0和1的总个数 }; node stk[maxn]; double sum[maxn],a[maxn]; int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { scanf("%lf",&a[i]); sum[i] = sum[i-1]+a[i]; } int i = 1,k = n; while(a[i]==0) i++; while(a[k]==1) k--; int top = 0,le = i; node x,y; for(;i<=k;i++) { if(a[i]==0) { while(a[i]==0 && i<=k) //这里如果不加控制i<=k,i可能超出k i++; double a = sum[i-1]-sum[le-1]; double b = (double)i-le; while(top>0 && a/b<stk[top].v/stk[top].num) //这里也不能忘了top>0 { y = stk[top--]; a += y.v; b += y.num; } x.v = a; x.num = b; stk[++top] = x; le = i; } } double ans = 0; while(top) { y = stk[top--]; double val = y.v/y.num; ans += (1-val)*(1-val)*y.v + val*val*(y.num-y.v); } printf("%.6lf\n",ans); } return 0; }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。