您的位置:首页 > 其它

bzoj 2428 [HAOI2006]均分数据

2015-04-02 14:47 302 查看

Description

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

Solution

这题一眼看不会做啊,这题肯定是乱搞题啊。。。一看这n≤20n\leq20,想搜来着,然后感觉不太对。看了一眼别人的题解恍然大悟,模拟退火!真是长姿势了,以前总是拘泥于用退火做计算几何,没有深入理解退火的实质。真是too young。

先随机分组,然后随机选择一个元素x,当温度T比较高的时候说明答案并不稳定,这样贪心找一个组sumsum最小的组把x放进去;当温度T较小时说明答案比较稳定,随机一个组放进去即可

这里有些不理解的地方,因为退火在更改后状态不是更优的时候是有一定概率转移的,而这道题如果当转移状态后答案不是更优的时候我采取干脆不转移的方式跑的反而更快。而在计算几何也中通常采取转移后没有变的更优就干脆不转移的方式。到底要不要在答案没变优的时候还以一定概率转移呢?= =

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define pb push_back
#define mp make_pair
#define F first
#define S second
inline void read(int &t) {
int f = 1;char c;
while (c = getchar(), c < '0' || c > '9') if (c == '-') f = -1;
t = c - '0';
while (c = getchar(), c >= '0' && c <= '9') t = t * 10 + c - '0';
t *= f;
}
const int N = 25;
int n, m, pos
;
double a
, sum
;
int main() {
srand(65535);
read(n), read(m);
double avg = 0.0;
for (int i = 1; i <= n; ++i) scanf("%lf", &a[i]), avg += a[i];
random_shuffle(a + 1, a + n + 1);
avg /= m;
double t = 1e20;
for (int i = 1; i <= 3000; ++i) {
memset(sum, 0, sizeof(sum));
double ans = 0;
for (int j = 1; j <= n; ++j) pos[j] = rand() % m + 1, sum[pos[j]] += a[j];
for (int j = 1; j <= m; ++j) ans += (sum[j] - avg) * (sum[j] - avg);
for (double T = 2000; T >= 0.1; T *= 0.9) {
int x = rand() % n + 1, p = pos[x], y;
if (T > 500) y = min_element(sum + 1, sum + m + 1) - sum;
else y = rand() % m + 1;
if (p == y) continue;
double pre = ans;
ans -= (sum[p] - avg) * (sum[p] - avg);
ans += (sum[p] - a[x] - avg) * (sum[p] - a[x] - avg);
ans -= (sum[y] - avg) * (sum[y] - avg);
ans += (sum[y] + a[x] - avg) * (sum[y] + a[x] - avg);
sum[p] -= a[x], sum[y] += a[x];
t = min(t, ans);
if (ans <= pre)  pos[x] = y;
else sum[p] += a[x], sum[y] -= a[x], ans = pre;//不转移
/*  else if (rand() >= exp((ans - pre) / T)) {一定概率转移
sum[p] += a[x], sum[y] -= a[x];
ans = pre;
}
else pos[x] = y;*/
}
}
printf("%.2lf\n", sqrt(t / m));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: