您的位置:首页 > 其它

[BZOJ 2428] [HAOI2006] 均分数据

2015-03-16 20:14 232 查看
题目链接: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\n", 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\n", Ans);
return 0;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: