经过了14次的提交,终于正大光明的把本题A掉了。。。。
算法:
主要思想是模拟退火,具体操作就是,我们对于每一个生成的排列,用连续分组DP处理此情况下的最小均方差。
dp[i][j]表示前i个数中分j组的最小均方差,dp[i][j]=min(dp[i][j],dp[k-1][j-1]+prefix[i]-prefix[j-1]-prefix[n]/m (1<=k<=i))
(prefix是前缀和应该很显然吧。。。。。)
Code:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(register int i=j;i<=k;i++)
int n,m;
int co[30];
template<typename T> void chkmin(T &x,T y){x=x<y?x:y;}
template<typename T> void read(T &num){
char c=getchar();num=0;T f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
num*=f;
}
int temp1[30];int temp2[30];
double dp[30][30];double prefix[30];
inline double work(int a[]){
memset(dp,127,sizeof(dp));
rep(i,1,n){prefix[i]=prefix[i-1]+co[a[i]];}
dp[0][0]=0;
rep(i,1,n){
rep(j,1,min(i,m)){
rep(k,1,i){
double evenbao=(prefix[i]-prefix[k-1]-prefix[n]*1.0/m);
chkmin(dp[i][j],dp[k-1][j-1]+evenbao*evenbao);
}
}
}
return dp[n][m];
}
inline bool Okay(double x,double y){
if(x>=0)return true;
return rand()<=exp((-x)/y)*RAND_MAX;
}
inline double SA(){
double temper=10000;
random_shuffle(temp1+1,temp1+n+1);
rep(i,1,n){temp2[i]=temp1[i];}
double nop1=work(temp1);double re_value=nop1;
while(temper>=0.01){
int x=rand()%n+1;int y=rand()%n+1;
if(x==y)continue;
swap(temp2[x],temp2[y]);
double nop2=work(temp2);chkmin(re_value,nop2);
if(Okay(nop1-nop2,temper)){
nop1=nop2;swap(temp1[x],temp1[y]);
}else{
swap(temp2[x],temp2[y]);
}
temper*=0.99;
}
return re_value;
}
int main(){
srand(time(NULL));
read(n);read(m);
rep(i,1,n){read(co[i]);}
rep(i,1,n){temp1[i]=i;}
double ans=INT_MAX;
rep(i,1,72){
chkmin(ans,SA());
}
double temp=m;
printf("%.2f\n",(sqrt(ans/temp)));
return 0;
}