题目链接
题面
题意:
给你一个长度为n的序列a,对于每一个元素,你可以更改其为任何值,但是代价为他们差值的平方,问你把这个序列改成等差数列的最小花费是多少
分析:
其实如果看这种最小值的题的话很容易想到的就是三分,因为三分他的函数曲线是呈二次函数型的,有一个极值,所以说可以用二次函数做,就是三分等差d,然后看左边三分之一处的d值代表的最小花费和右边三分之一处的最小花费来作比较,这个是三分的常用思路,然后在三分的判断函数里,是一个二次函数的曲线,只需要找到最值就行,但是这个精度卡的特别死,不能直接乘起来,要慢慢加起来,这也算是一种技巧把,那么看一下三分极值的代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define double long double
const int N=1e6+10;
int n;
double a[N],eps=1e-10;
double c[N];
namespace GTI
{
char gc(void)
{
const int S = 1 << 16;
static char buf[S], *s = buf, *t = buf;
if (s == t) t = buf + fread(s = buf, 1, S, stdin);
if (s == t) return EOF;
return *s++;
}
int read(void)
{
int a = 0, b = 1, c = gc();
for (; !isdigit(c); c = gc()) b ^= (c == '-');
for (; isdigit(c); c = gc()) a = a * 10 + c - '0';
return b ? a : -a;
}
}
using GTI::read;
double cal(double d)
{
double ans=0,b=0;
for(int i=1;i<=n;i++)
{
b += a[i]-(i-1)*d;
}
b /= n;
for(int i=1;i<=n;i++){
double t = a[i]-(i-1)*d;
ans += (t-b)*(t-b);
}
return ans;
}
int main()
{
int T;
cin>>T;
while(T--)
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();
double l=-1e9,r=1e9;
while(fabs(r-l)>eps)
{
double lmid=l+(r-l)/3,rmid=r-(r-l)/3;
if(cal(lmid)>cal(rmid)) l=lmid;
else r=rmid;
}
printf("%.10Lf\n",cal(l));
}
return 0;
}
最小二乘法
等差数列可以看作是一条直线,然后相当于线性拟合,就把高中学过的最小二乘法给搬过来就行,但是注意要用那个乘法少的公式,那样损失的精度会小,下面请看代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define double long double
const int N=1e6+10;
int n;
double a[N];
int read() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main()
{
int T;
cin>>T;
while(T--)
{
n=read();
double x=(n+1)/2.0;
double y=0,t=0,tx=0;
for(int i=1;i<=n;i++)
{
a[i]=read();
y+=a[i];
t+=i*a[i];
tx+=i*i;
}
y/=n;
double k=0;
for(int i=1;i<=n;i++) k+=(i-x)*(a[i]-y);
double ttt=0;
for(int i=1;i<=n;i++) ttt+=(x-i)*(x-i);
k/=ttt;
double b=y-k*x;
double ans=0;
double tt=k+b;
for(int i=1;i<=n;i++)
{
ans+=(a[i]-tt)*(a[i]-tt);
tt+=k;
}
printf("%.10Lf\n",ans);
}
return 0;
}