[BZOJ 2428] [HAOI2006] 均分数据

题目链接:BZOJ - 2428

题目分析

这道题使用随机化算法来做,可以使用模拟退火,也可以random_shuffle之后贪心。

模拟退火:

要进行多次模拟退火,每次进行模拟退火之前,给每个点随机分配一下集合。

然后模拟退火的随机移动就是随机一个点,再找另一个集合y,将这个点移动到集合y中。

开始时模拟退火的移动导致答案的变动幅度会比较大,这时候集合y就直接取最小的集合,到后期就随机一个集合y。

注意,如果移动之后答案与当前答案相同,也要移动过去。这是为了避免滞留在平坦的高原地形上

random_shuffle + 贪心:

多次进行random_shuffle + 贪心的过程,可能就会找到最优解了= =

代码

模拟退火:

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxN = 20 + 5;

typedef double DB;

int n, m;
int A[MaxN], Belong[MaxN];

DB Ans, Ave;
DB Sum[MaxN];

inline DB Sqr(DB x) {return x * x;}

inline DB Rand() 
{
	return (DB)(rand() % 10000) / 10000.0;
}

void SA() 
{
	memset(Sum, 0, sizeof(Sum));
	for (int i = 1; i <= n; ++i) 
	{	
		Belong[i] = rand() % m + 1;
		Sum[Belong[i]] += (DB)A[i];
	}
	DB T = 10000;
	int t, x, y;
	DB DE, Temp, Num;
	Num = 0;
	for (int i = 1; i <= m; ++i) Num += Sqr(Sum[i] - Ave);
	if (Num < Ans) Ans = Num;
	while (T > 0.1)
	{
		T *= 0.9;
		t = rand() % n + 1;
		x = Belong[t];
		if (T > 500)
		{
			y = 1; Temp = Sum[1];
			for (int i = 2; i <= m; ++i) 
			{
				if (Sum[i] < Temp)
				{
					Temp = Sum[i];
					y = i;
				}
			}
		}
		else y = rand() % m + 1;
		if (y == x) continue;
		Temp = Num;
		Temp -= Sqr(Sum[x] - Ave) + Sqr(Sum[y] - Ave);
		Temp += Sqr(Sum[x] - A[t] - Ave) + Sqr(Sum[y] + A[t] - Ave);
		if (Temp < Ans) Ans = Temp;
		DE = Num - Temp;
		if (DE >= 0 || Rand() < exp(DE / T))
		{
			Num = Temp;
			Sum[x] -= A[t];
			Sum[y] += A[t];
			Belong[t] = y;
		}
	}
	if (Num < Ans) Ans = Num;
}

int main()
{
	srand(80458946);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) 
	{	
		scanf("%d", &A[i]);
		Ave += (DB)A[i];
	}
	Ave /= m;
	Ans = 1e50;
	for (int i = 1; i <= 10000; ++i) SA();
	printf("%.2lf
", sqrt(Ans / m));
	return 0;
}

 

random_shuffle + 贪心:

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxN = 20 + 5;

typedef double DB;

int n, m;
int A[MaxN];

inline DB Sqr(DB x) {return x * x;}

DB Ave, Ans;

DB Solve()
{
	DB p, q, Sum = 0, ret = 0, Cnt = 1;
	int i;
	for (i = 1; i <= n && Cnt < m; ++i)
	{
		if (Sum + (DB)A[i] >= Ave)
		{
			p = Sqr(Sum + (DB)A[i] - Ave);
			q = Sqr(Sum - Ave);
			if (p < q) 
			{
				ret += p;
				Sum = 0;
			}
			else
			{
				ret += q;
				Sum = A[i];
			}
			++Cnt;
		}
		else Sum += (DB)A[i];
	}
	for (; i <= n; ++i) Sum += (DB)A[i];
	ret += Sqr(Sum - Ave);
	return sqrt(ret / m);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) 
	{	
		scanf("%d", &A[i]);
		Ave += (DB)A[i];
	}
	Ave /= m;
	Ans = 1e50;
	for (int i = 1; i <= 500000; ++i)
	{
		random_shuffle(A + 1, A + n + 1);
		Ans = min(Ans, Solve());
	}
	printf("%.2lf
", Ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/JoeFan/p/4342703.html