模拟退火+dp
(不得不说,我今天欧气爆棚)
随机出1个数列,然后跑一遍dp统计
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<ctime>
#define re register
using namespace std;
template <typename T> inline T min(T &a,T &b) {return a<b ?a:b;}
template <typename T> inline T max(T &a,T &b) {return a>b ?a:b;}
template <typename T> inline void read(T &x){
char c=getchar(); x=; bool f=;
while(!isdigit(c)) f= !f||c=='-' ? :,c=getchar();
while(isdigit(c)) x=(x<<)+(x<<)+(c^),c=getchar();
x= f ? x:-x;
}
template <typename T> inline T squ(T a) {return a*a;}
typedef double db;
int n,m,a[],sum[];
db f[][],ans=1e15,ave;
inline db calc(){ //dp:设f[i][j]表示当前为第i个位置,分成j块
memset(f,,sizeof(f)); f[][]=;
for(re int i=;i<=n;++i) sum[i]=sum[i-]+a[i];
for(re int i=;i<=n;++i)
for(re int j=;j<=i;++j)
for(re int k=;k<i;++k)
f[i][j]=min(f[i][j],f[k][j-]+squ(sum[i]-sum[k]-ave));
return f[n][m];
}
inline void smt(){
for(db t=;t>1e-;t*=0.993){ //自行修改
int x=rand()%n+,y=rand()%n+;
swap(a[x],a[y]); //随机交换2个数字
db _val=calc(),delta=_val-ans;
if(delta<) ans=_val;
else if(exp(-delta/t)*RAND_MAX<=rand()) swap(a[x],a[y]); //一定概率接受非最优解
}
} int main(){
srand(); srand(rand()); srand(rand());
read(n),read(m);
for(re int i=;i<=n;++i) read(a[i]),ave+=a[i];
ave=ave/(db)m;
while((db)clock()/CLOCKS_PER_SEC<0.825) smt(); //卡时。可以先看下跑一遍退火时间多少,再修改限制值(记得多留点时间给下面的代码)
printf("%.2lf",sqrt(ans/(db)m));
return ;
}