BZOJ2428 HAOI2006均分数据(模拟退火)
显然可以状压dp。显然过不了。
考虑暴力模拟退火。每次随机改变一个数所属集合即可。
并不明白要怎么调参。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 21 #define T0 1E5 #define T1 1E-6 #define v 0.996 int n,m,a[N],sum[N],id[N],size[N]; double ave,ans=100000000; void anneal() { for (int i=1;i<=m;i++) id[i]=i,sum[i]=a[i],size[i]=1; for (int i=m+1;i<=n;i++) id[i]=rand()%m+1,sum[id[i]]+=a[i],size[id[i]]++; double T=T0,tot=0; for (int i=1;i<=m;i++) tot+=(sum[i]-ave)*(sum[i]-ave); if (m<n) while (T>T1) { int x=rand()%n+1; while (size[id[x]]==1) x=rand()%n+1; int y=rand()%m+1; while (id[x]==y) y=rand()%m+1; if (rand()<exp((-2.0*a[x]*(a[x]-sum[id[x]]+sum[y]))/T)*RAND_MAX) { tot+=2*a[x]*(a[x]-sum[id[x]]+sum[y]); sum[y]+=a[x],sum[id[x]]-=a[x]; size[y]++,size[id[x]]--; id[x]=y; } T*=v; ans=min(ans,tot); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2428.in","r",stdin); freopen("bzoj2428.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif srand(20020509); n=read(),m=read(); for (int i=1;i<=n;i++) ave+=a[i]=read(); ave/=m; for (int i=1;i<=200;i++) anneal(); printf("%.2lf",sqrt(ans/m)); return 0; }