【Luogu P5752】[NOI1999] 棋盤分割
阿新 • • 發佈:2021-07-08
【Luogu P5752】[NOI1999] 棋盤分割
連結:
題目大意:
將一個 8 \(\times\) 8 的棋盤進行如下分割:將原棋盤割下一塊矩形棋盤並使剩下部分也是矩形,再將剩下的部分繼續如此分割,這樣割了 \((n-1)\) 次後,連同最後剩下的矩形棋盤共有 \(n\) 塊矩形棋盤。 (每次切割都只能沿著棋盤格子的邊進行)
原棋盤上每一格有一個分值,一塊矩形棋盤的總分為其所含各格分值之和。現在需要把棋盤按上述規則分割成 \(n\) 塊矩形棋盤,並使各矩形棋盤總分的均方差最小。
均方差 \(\sigma = \sqrt{ \frac{ \sum_{i=1}^n (x_i - \bar x)^2 } { n }}\)
請程式設計對給出的棋盤及 \(n\) ,求出 \(\sigma\) 的最小值。
正文:
本題可以考慮先求出方差,在求出答案後輸出其開根的結果,這樣比較好算。
然後考慮二位區間 DP,其實思路很好想,用記憶化搜尋做。
程式碼:
const int N = 20; inline ll Read() { ll x = 0, f = 1; char c = getchar(); while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') f = -f, c = getchar(); while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar(); return x * f; } int n; int a[N][N]; long double f[N][N][N][N][N], barx; long double Sum(int x1, int y1, int x2, int y2) { long double x = a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1] - barx; return x * x / n; } long double DFS(int x1, int y1, int x2, int y2, int k) { if (f[x1][y1][x2][y2][k] >= 0) return f[x1][y1][x2][y2][k]; if (k == 1) return f[x1][y1][x2][y2][k] = Sum(x1, y1, x2, y2); f[x1][y1][x2][y2][k] = 1e9; for (int i = x1; i < x2; i++) f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k], DFS(x1, y1, i, y2, k - 1) + Sum(i + 1, y1, x2, y2)), f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k], DFS(i + 1, y1, x2, y2, k - 1) + Sum(x1, y1, i, y2)); for (int i = y1; i < y2; i++) f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k], DFS(x1, y1, x2, i, k - 1) + Sum(x1, i + 1, x2, y2)), f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k], DFS(x1, i + 1, x2, y2, k - 1) + Sum(x1, y1, x2, i)); return f[x1][y1][x2][y2][k]; } int main() { n = Read(); for (int i = 1; i <= 8; i++) for (int j = 1; j <= 8; j++) a[i][j] = Read(), a[i][j] += a[i][j - 1] + a[i - 1][j] - a[i - 1][j - 1]; barx = a[8][8] * 1.0 / n; memset (f, -1, sizeof f); double ans = DFS(1, 1, 8, 8, n); ans = sqrt(ans); printf ("%.3f\n", ans); return 0; }