Description
Solution
对于一种确定的排列,我们可以用 (O(n^2*k)) 的 (DP) 算出最优划分的方法
但是排列需要枚举,我们可以考虑退火
每一次交换两个元素,跑一边 (DP) 求答案即可
#include<bits/stdc++.h>
#define sqr(x) ((x)*(x))
using namespace std;
const int N=25;
inline int rd(int l,int r){return l+rand()%(r-l+1);}
int n,m,a[N],b[N],s[N];double p,f[N][7],inf=1e15;
inline double calc(){
f[0][0]=0;
for(int i=1;i<=n;i++)s[i]=s[i-1]+b[i],f[i][0]=inf;
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++){
f[i][j]=inf;
for(int k=0;k<i;k++)
f[i][j]=min(f[i][j],f[k][j-1]+sqr(s[i]-s[k]-p));
}
return f[n][m]/m;
}
double maxt=100000,mint=1e-15,Down=0.993,ans=inf;
inline void solve(){
double t=maxt,tans,sans=inf;
while(t>mint){
int x=rd(1,n-1),y=rd(x+1,n);
swap(b[x],b[y]);
tans=calc();
if(tans<sans)sans=tans;
else if(exp((ans-tans)/t)*RAND_MAX<rand())swap(b[x],b[y]);
else sans=tans;
ans=min(ans,sans);t*=Down;
}
ans=min(ans,sans);
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
srand(19260859);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
p=(double)s[n]/m;
int T=5;
while(T--){
for(int i=1;i<=n;i++)b[i]=a[i];
solve();
}
printf("%.2lf
",sqrt(ans));
return 0;
}