【bzoj2428】[HAOI2006]均分数据

2428: [HAOI2006]均分数据

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 2944  Solved: 916
[ 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]


HOME Back




模拟退火入门题。。。。


先随机生成一个解,

然后每次考虑产生一个新解,具体来说就是随机选一个数,再转移到随机一个组里,计算均方差

接下来考虑是否接纳这个解:如果这个解比当前解优,那么接纳这个解;否则按一定的概率接纳这个解,这个概率是随着时间的推移而逐渐减小的,


这样的过程中解趋于稳定,越来越趋近于正确答案


但是考虑到当当前温度很高时,解的走向不稳定,很有可能越变越劣,那么我们考虑在高温时加一个贪心,来保证前期解的方向一定是往更优的方向走,具体说来即把选出来的这个数放到和值最小的那一组里去


PS.蒟蒻做这题时题目看错调了一个小时。。。。。。初温设得不同在bzoj上的答案也是不一样的。。。。


代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;
typedef long double DL;

const DL d = 0.99,eps = 0.1;
const int INF = 2147483647;
const int maxn = 30;

int n,m,be[maxn],a[maxn];
DL sum[maxn],now,ave,ans,s;

inline int RAND()
{
	return rand() % 10000;
}

inline int RANDTO(int t)
{
	int k = rand() % m + 1;
	while (k == t) k = rand() % m + 1;
}

inline DL sqr(DL x){return x * x;}

inline void backout()
{
	memset(sum,0,sizeof(sum));
	now = 0;
	for (int i = 1; i <= n; i++)
	{
		int to = rand() % m + 1;
		sum[to] += a[i]; be[i] = to;
	}
	for (int i = 1; i <= m; i++) now += sqr(sum[i] - ave);
	DL T = 100000;
	while (T > eps)
	{
		int u = rand() % n + 1,from = be[u],to;
		if (T > 500)
		{
			DL minx = INF;
			for (int i = 1; i <= m; i++)
			{
				if (i == from) continue;
				if (sum[i] < minx)
				{
					minx = sum[i];
					to = i;
				}
			}
		}
		else to = rand() % m + 1;
		DL pre = now;
		now -= sqr(sum[from] - ave);
		now -= sqr(sum[to] - ave);
		
		sum[from] -= a[u]; sum[to] += a[u];
		
		now += sqr(sum[from] - ave); 
		now += sqr(sum[to] - ave);
		if (pre < now && RAND() > T)
		{
			sum[from] += a[u];
			sum[to] -= a[u];
			now = pre;
		}
		else be[u] = to;
		T *= d;
	}
	ans = min(ans,now);
}

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

int main()
{
	#ifdef dyf
		freopen("hyj1.txt","r",stdin);
	#endif
	n = getint(); m = getint();
	for (int i = 1; i <= n; i++) a[i] = getint() , s += a[i];
	ans = INF; ave = s / m;
	for (int i = 1; i <= 1000; i++) backout();
	printf("%0.2Lf",sqrt(ans / m));
	return 0;
}


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