首先很容易想到给原数组排序,于是最大值和最小值就变成最左和最右。明显是选取连续的区间最优,于是原问题就转化为了区间DP,设前
i
i
个中选取个区间的最优解为
d[i][j]
d
[
i
]
[
j
]
,转移方程:
d[i][j]=min(d[i−1][k]+(s[j]−s[k])2)
d
[
i
]
[
j
]
=
m
i
n
(
d
[
i
−
1
]
[
k
]
+
(
s
[
j
]
−
s
[
k
]
)
2
)
其中s是前缀和
然而复杂度过大,于是我们需要斜率优化。原式展开:
d[i][j]=d[i−1][k]+s[j]2−2s[j]s[k]+s[k]2
d
[
i
]
[
j
]
=
d
[
i
−
1
]
[
k
]
+
s
[
j
]
2
−
2
s
[
j
]
s
[
k
]
+
s
[
k
]
2
移项:
d[i−1][k]+s[k]2=2s[j]∗s[k]+d[i][j]−s[j]2
d
[
i
−
1
]
[
k
]
+
s
[
k
]
2
=
2
s
[
j
]
∗
s
[
k
]
+
d
[
i
]
[
j
]
−
s
[
j
]
2
于是就变成了可以斜率优化的形式:
y=d[i−1][k]+s[k]2 y = d [ i − 1 ] [ k ] + s [ k ] 2
x=s[k] x = s [ k ]
k=2∗s[j] k = 2 ∗ s [ j ]
b=d[i][j]−s[j]2 b = d [ i ] [ j ] − s [ j ] 2
单调队列维护一波凸函数就可以啦。具体见代码。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n,m,t,a[10010],s[10010],d[2][10010],q[10010];
int dp()
{
for(int i=1;i<=n;i++)
{
d[1][i]=(a[i]-a[1])*(a[i]-a[1]);
}
for(int i=2,cur=0;i<=m;i++,cur^=1)
{
int head=0,tail=0;
q[tail++]=i-1;
for(int j=i;j<=n;j++)
{
while(head+1<tail)
{
int y1=d[cur^1][q[head]]+a[q[head]+1]*a[q[head]+1];
int x1=a[q[head]+1];
int y2=d[cur^1][q[head+1]]+a[q[head+1]+1]*a[q[head+1]+1];
int x2=a[q[head+1]+1];
if(y1-2*a[j]*x1>=y2-2*a[j]*x2)head++;
else break;
}
d[cur][j]=d[cur^1][q[head]]+(a[j]-a[q[head]+1])*(a[j]-a[q[head]+1]);
// cout<<i<<' '<<j<<' '<<q[head]<<' '<<d[cur][j]<<' '<<d[cur^1][q[head]]<<endl;
while(head+1<tail)
{
int y1=(d[cur^1][j]+a[j+1]*a[j+1])-(d[cur^1][q[tail-1]]+a[q[tail-1]+1]*a[q[tail-1]+1]);
int x1=a[j+1]-a[q[tail-1]+1];
int y2=(d[cur^1][q[tail-1]]+a[q[tail-1]+1]*a[q[tail-1]+1])-(d[cur^1][q[tail-2]]+a[q[tail-2]+1]*a[q[tail-2]+1]);
int x2=a[q[tail-1]+1]-a[q[tail-2]+1];
if(y1*x2<=y2*x1)tail--;
else break;
}
q[tail++]=j;
}
}
return d[m&1][n];
}
int main() {
cin>>t;
for(int j=1;j<=t;j++)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
printf("Case %d: %d\n",j,dp());
}
return 0;
}