[luogu2503][HAOI2006]均分數據
阿新 • • 發佈:2019-03-14
如果 sizeof sqrt 均方差 最小 種子 put haoi2006 main
我給出一個比較優秀的隨機種子,是ouhuang和6666666的取模,就是15346301。
題目描述
已知N個正整數:A1、A2、……、An 。今要將它們分成M組,使得各組數據的數值和最平均,即各組的均方差最小。均方差公式如下:
分析
萬物皆可頹火,我們首先將初始的答案當做一半一半的答案,然後我們隨機化抽取兩個部分的數據。根據題目中的描述,因為兩個組別之間數據個數只能是差一,那麽差不多就是一半一半的情況。那麽我們就只需要分塊兩部分,然後隨機交換,如果兩個數據交換之後能使答案能更優,那麽就交換,如果不能讓我們的答案變得更加優,那麽就讓隨機概率,這個概率很明顯是越到後面交換的概率越小,那麽我們就是exp(delta) < t * Rand()
,那麽就交換,否則就不交換。
模擬退火的精髓還是這個調參,這道題目我一遍A掉了,感覺有一點歐皇。
ac代碼
#include <bits/stdc++.h> #define ms(a,b) memset(a, b, sizeof(a)) #define db double using namespace std; inline char gc() { static char buf[1 << 16], *S, *T; if (S == T) { T = (S = buf) + fread(buf, 1, 1 << 16, stdin); if (T == S) return EOF; } return *S ++; } template <typename T> inline void read(T &x) { T w = 1; x = 0; char ch = gc(); while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = gc(); } while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc(); x = x * w; } template <typename T> void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + 48); } #define N 305 db ans = 1e30, ave = 0; int sum[N], pos[N], a[N]; int n, m; void SA(db T){ ms(sum, 0); for (int i = 1; i <= n; i ++) { pos[i] = rand() % m + 1; sum[pos[i]] += a[i]; } db res = 0; for (int i = 1; i <= m; i ++) res += (1.0 * sum[i] - ave) * (1.0 * sum[i] - ave); while (T > 1e-4) { int t = rand() % n + 1, x = pos[t], y; if (T > 500) y = min_element(sum + 1, sum + 1 + m) - sum; else y = rand() % m + 1; if (x == y) continue; db tmp = res; res -= (sum[x] - ave) * (sum[x] - ave); res -= (sum[y] - ave) * (sum[y] - ave); sum[x] -= a[t], sum[y] += a[t]; res += (sum[x] - ave) * (sum[x] - ave); res += (sum[y] - ave) * (sum[y] - ave); if (res < tmp || rand() % 10000 <= T) pos[t] = y; else sum[x] += a[t], sum[y] -= a[t], res = tmp; ans = min(ans, res); T *= 0.98; } } int main() { srand(20040127); read(n); read(m); for (int i = 1; i <= n; i ++) { read(a[i]); ave += 1.0 * a[i]; } ave /= 1.0 * m; for (int i = 1; i <= 1500; i ++) SA(10000); printf("%.2lf\n", sqrt(ans / m)); return 0; }
[luogu2503][HAOI2006]均分數據