P2503 [HAOI2006]均分数据
模拟退火+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=0; bool f=1; while(!isdigit(c)) f= !f||c=='-' ? 0:1,c=getchar(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); x= f ? x:-x; } template <typename T> inline T squ(T a) {return a*a;} typedef double db; int n,m,a[23],sum[23]; db f[23][23],ans=1e15,ave; inline db calc(){ //dp:设f[i][j]表示当前为第i个位置,分成j块 memset(f,127,sizeof(f)); f[0][0]=0; for(re int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i]; for(re int i=1;i<=n;++i) for(re int j=1;j<=i;++j) for(re int k=0;k<i;++k) f[i][j]=min(f[i][j],f[k][j-1]+squ(sum[i]-sum[k]-ave)); return f[n][m]; } inline void smt(){ for(db t=2000;t>1e-14;t*=0.993){ //自行修改 int x=rand()%n+1,y=rand()%n+1; swap(a[x],a[y]); //随机交换2个数字 db _val=calc(),delta=_val-ans; if(delta<0) ans=_val; else if(exp(-delta/t)*RAND_MAX<=rand()) swap(a[x],a[y]); //一定概率接受非最优解 } } int main(){ srand(19260817); srand(rand()); srand(rand()); read(n),read(m); for(re int i=1;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 0; }