bzoj2428 [HAOI2006]均分数据 模拟退火

[HAOI2006]均分数据

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 3434  Solved: 1091
[Submit][Status][Discuss]

Description

已知N个正整数:A1、A2、……、An 。今要将它们分成M组,使得各组数据的数值和最平均,即各组的均方差最小。均方差公式如下:

,其中σ为均方差,是各组数据和的平均值,xi为第i组数据的数值和。

Input

第一行是两个整数,表示N,M的值(N是整数个数,M是要分成的组数)
第二行有N个整数,表示A1、A2、……、An。整数的范围是1--50。
(同一行的整数间用空格分开)

Output

这一行只包含一个数,表示最小均方差的值(保留小数点后两位数字)。

 

 

 

 

Sample Input

6 3
1 2 3 4 5 6

Sample Output

0.00

HINT

 

对于全部的数据,保证有K<=N <= 20,2<=K<=6

 

Source

[Submit][Status][Discuss]


HOME Back

 

以前也没怎么写过模拟退火,这道题让我知道了一些怎么写

退了10000次火,模拟退火主要靠感觉的吧,

写法也是相当奇怪的

当温度很高的时候,将随机出来的一个数,放入当前组里最小的一组,

当温度比较低的时候,就随机放在一组里,然后判断交换后答案是否更优,是的话就交换,不然随机交换。

 1 #include<cstring>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 
 7 #define N 10007
 8 #define ll long long
 9 using namespace std;
10 inline int read()
11 {
12     int x=0,f=1;char ch=getchar();
13     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
14     while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
15     return x*f;
16 }
17 
18 int n,m;
19 int sum[N],a[N],belong[N];
20 double minans=1e30,ave;
21 
22 void SA()
23 {
24     memset(sum,0,sizeof(sum));
25     for (int i=1;i<=n;i++)
26     {
27         belong[i]=rand()%m+1;
28         sum[belong[i]]+=a[i];
29     }
30     double ans=0;
31     for (int i=1;i<=m;i++)
32         ans+=(sum[i]-ave)*(sum[i]-ave);
33     double T=10000;
34     while(T>0.1)
35     {
36         T*=0.9;
37         int t=rand()%n+1,x=belong[t],y;
38         if (T>500) y=min_element(sum+1,sum+m+1)-sum;
39         else y=rand()%m+1;
40         if (x==y) continue;
41         double tmp=ans;
42         ans-=(sum[x]-ave)*(sum[x]-ave);
43         ans-=(sum[y]-ave)*(sum[y]-ave);
44         sum[x]-=a[t],sum[y]+=a[t];
45         ans+=(sum[x]-ave)*(sum[x]-ave);
46         ans+=(sum[y]-ave)*(sum[y]-ave);
47         if (ans<=tmp) belong[t]=y;
48         else if (rand()%10000>T) sum[x]+=a[t],sum[y]-=a[t],ans=tmp;
49         else belong[t]=y;
50     }
51     if (ans<minans) minans=ans;
52 }
53 int main()
54 {
55     srand(19260817);
56     n=read(),m=read();
57     for (int i=1;i<=n;i++)
58         a[i]=read(),ave+=a[i];
59     ave/=(double)m;
60     for (int i=1;i<=10000;i++) SA();
61     printf("%.2lf\n",sqrt(minans/m));
62 }

 

posted @ 2018-04-08 17:05  Kaiser-  阅读(165)  评论(0编辑  收藏  举报