2428: [HAOI2006]均分数据

2428: [HAOI2006]均分数据

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 1660  Solved: 500
[ 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

Day2

[ Submit][ Status][ Discuss]

RP算法--模拟退火
主要思路如下:
随机选出一组解,,此时可能离正解很遥远,我们随机地去更新,随着时间的推移,这个解离正解的距离也会越来越近,最后停止,即得出正解
一开始随意地把元素置入各个分组,每次随机一个元素,将它放到别的组里面去,一开始我们可能离正解很远,就贪心找一个当前元素和最小的分组,把这个元素放进去,看能不能更优,随着时间的推移,离正解越来越近,此时贪心不一定可行,就随机一个分组,把这个元素放进去
当改变了一个元素所属分组,如果新的ans优于当前的,就接受它,否则以一定概率接受它,这个概率一定是随着时间推移不断降低的
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef double DB;

int n,m,a[22],belong[22];
DB Ave,ans = 1E18,sum[10];

DB Pow(DB x) {return x*x;}
void Fire()
{
	for (int i = 1; i <= m; i++)
		sum[i] = 0;
	for (int i = 1; i <= n; i++) {
		int Num = rand()%m + 1;
		sum[Num] += a[i];
		belong[i] = Num;
	}
	DB Now = 0;
	for (int i = 1; i <= m; i++)
		Now += Pow(sum[i] - Ave);
	
	DB T = 10000;
	while (T > 0.1) {
		T *= 0.9;
		int x = rand()%n + 1;
		DB tmp = Now,k = a[x];
		int A = belong[x],B;
		if (T > 500) {
			DB Min = 1E18;
			for (int i = 1; i <= m; i++)
				if (sum[i] < Min)
					Min = sum[i],B = i;
		}
		else B = rand()%m + 1;
		if (A == B) continue;
		Now -= Pow(sum[A] - Ave);
		Now -= Pow(sum[B] - Ave);
		sum[A] -= k;
		sum[B] += k;
		Now += Pow(sum[A] - Ave);
		Now += Pow(sum[B] - Ave);
		if (Now > tmp) {
			int pass = rand()%10000;
			if (pass > T) {
				sum[A] += k;
				sum[B] -= k;
				Now = tmp;
				continue;
			}
		}
		belong[x] = B;
	}
	ans = min(ans,Now);
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	//srand(19990720);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
		Ave += (DB)(a[i]);
	}
	Ave /= (DB)(m);
	for (int i = 1; i <= 10000; i++) 
		Fire();
	printf("%.2lf",sqrt(ans/(DB)(m)));
	return 0;
}

你可能感兴趣的:(模拟退火)