bzoj 2428: [HAOI2006]均分数据

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;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8719291.html