题意
给一个序列,对于其中的元素可以改变为任意值,花费为差值的平方,问将其改编为等差序列的最小花费
思路
假设之前的序列为a1,a2,a3……
修改后的序列为b1,b2,b3……
首项为b1,公差为d
差值ci=ai-bi
bi=b1+(i-1)d
答案就是c1c1+c2c2+……=
(a1-(b1+(i-1)d))^2+ (a2-(b1+(i-1)d))^2+……
然后化简出来发现把公差d当作未知数得到二元一次函数
三分d来缩小范围 check的时候计算代价
知道d以后
ci=ai-bi=ai-(b1+(i-1)d)
ci+b1都是未知的,放到一侧
ci+b1=ai-(i-1)d=di
c1c1+c2c2+c3c3+……
=(d1-b1)^2+ (d2-b1)^ 2+(d3-b1)^2+……
=nb1b1-2(d1+d2+d3+……)b1+(d1 * d1+d2 * d2+d3 * d3+……)
b1是未知数
相当于ax^2+by+c求极值,当x=-b/2a时取得极值
b1=(-2(d1+d2+d3+……))/(-2n)=(d1+d2+d3+……)/n
然后在计算ci就行了
ci=di-b1
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; long double a[maxn]; long double b[maxn]; long double eps=1e-10; int n; long double check(long double d){ long double res=0,sum=0; for(int i=1;i<=n;i++){ b[i]=(a[i]-(i-1)*d); sum=sum+b[i]; } sum/=n; for(int i=1;i<=n;i++){ res=res+(b[i]-sum)*(b[i]-sum); } return res; } int main() { int _;scanf("%d",&_); while(_--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%Lf",&a[i]); long double l=-1e10,r=1e10; while(r-l>eps){ long double mid1=l+(r-l)/3.0,mid2=r-(r-l)/3.0; if(check(mid1)>check(mid2)) l=mid1; else r=mid2; //cout<<_<<" "<<l<<" "<<r<<endl; } printf("%.10Lf\n",check(l)); } return 0; }