[HAOI2006]均分資料
阿新 • • 發佈:2018-12-17
經過了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; }