正題
題目鏈接:https://loj.ac/problem/2035
題目大意
個數字分成段,要求方差最小。
解題思路
首先方差的公式
其中是不變的,定義
設表示已經分到第段,到第個時的最小方差和。
做前綴和
之後有
去掉拆括號
求最小就是最小,後爲了方便
定義
然後有若干個決策點
每次有一條直線經過某個決策點要求最小
顯然因爲的單調性和的單調性我們可以使用單調隊列維護一個下凸殼。
時間複雜度
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pow2(x) ((x)*(x))
using namespace std;
const int N=3100;
struct node{
double x,y;
int num;
}q[N];
int n,m;
double s[N],f[N][N];
double slope(node x,node y)
{return (y.y-x.y)/(y.x-x.x);}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lf",&s[i]),s[i]=s[i]*m+s[i-1];
double w=s[n]/m;
for(int i=1;i<=n;i++)
f[1][i]=pow2(s[i]-w);
for(int k=2;k<=m;k++){
int head=1,tail=1;
q[1]=(node){s[k-1],f[k-1][k-1]+pow2(s[k-1]),k-1};
for(int i=k;i<=n;i++){
int z=2*(s[i]-w);
while(head<tail&&slope(q[head],q[head+1])<z)head++;
int p=q[head].num;
f[k][i]=f[k-1][p]+pow2(s[i]-s[p]-w);
node po=(node){s[i],f[k-1][i]+pow2(s[i]),i};
while(head<tail&&slope(po,q[tail])<slope(q[tail-1],q[tail]))
tail--;
q[++tail]=po;
}
}
printf("%.0lf",f[m][n]/m);
}