題目二 長方形
時間限制: 1000ms 內存限制: 256MB
描述
在 N × M 的網格上,放 K 枚石子,每個石子都只能放在網格的交叉點上。問在最優的擺放方式下,最多能找到多少四邊平行於坐標軸的長方形,它的四個角上都恰好放着一枚石子。
輸入
輸入文件包含多組測試數據。
第一行,給出一個整數T,為數據組數。接下來依次給出每組測試數據。
每組數據為三個用空格隔開的整數 N,M,K。
輸出
對於每組測試數據,輸出一行"Case #X: Y",其中X表示測試數據編號,Y表示最多能找到的符合條件的長方形數量。所有數據按讀入順序從1開始編號。
數據范圍
1 ≤ T ≤ 100
0 ≤ K ≤ N * M
小數據:0 < N, M ≤ 30
大數據:0 < N, M ≤ 30000
樣例輸入
3
3 3 8
4 5 13
7 14 86
樣例輸出
Case #1: 5
Case #2: 18
Case #3: 1398
解題思路
這道題算是一道數學題,將所有石子擺放成下面的形狀能組成最多的長方形應該是顯然的,其中有 $2 \le a \le N, 2 \le b \le M, r \ge 0, a \times b + r = K$。現在就是要求出最優的 $a, b, r$ 使得組成的長方形個數最多。
圖 1 石子擺放形式
一個最先需要解決的最基本的問題就是如何計算長方形的個數。對於 $a \times b$ 的石子組成的長方形,包含的所有長方形的個數為 $\frac{{ab(a - 1)(b - 1)}}{4}$,推導過程如下所示:
任何一個長方形都是由四個頂點組成的,但只要知道了左上角頂點和右下角頂點就可以唯一確定一個長方形,因此長方形的個數就等於唯一的左上角和右下角頂點對的個數。為了方便計算,設石子的最左上角位於坐標 $(1, 1)$,右下方為正方向,如圖所示。
圖 2 坐標軸
對於 $a \times b$ 的石子組成的長方形,可以作為左上角的頂點有 $(a-1)(b-1)$ 個(最底下的一行和最后邊的一列不能作為左上角頂點),設現在的左上角坐標為 $(x, y)$。對於每個左上角 $(x, y)$,相應的可作為右下角的頂點有 $(a - x)(b - y)$ 個(在上圖中,若紅色頂點為左上角,那么可能的右下角頂點一定是綠色的頂點之一),那么只要將所有左上角對應的右下角頂點的個數累加起來就是最后的結果,即為:
\[\sum\limits_{i = 1}^a {\sum\limits_{j = 1}^b {(a - i)(b - j)} } \]
這個公式求和很容易:
\[\sum\limits_{i = 1}^a {\sum\limits_{j = 1}^b {(a - i)(b - j)} } = \sum\limits_{i = 1}^a {(a - i)} \sum\limits_{j = 1}^b {(b - j)} = \frac{{a(a - 1)}}{2}\frac{{b(b - 1)}}{2} = \frac{{ab(a - 1)(b - 1)}}{4}\]
有了公式,那么就可以枚舉得到長方形個數的最大值了,為了簡單起見,可以令 $N \le M$。設長方形有 $a$ 行石子,那么枚舉的范圍是 $2 \le a \le \left\lceil {\sqrt K } \right\rceil$,$r = K % a$,長方形石子的列數 $b = (K-r) / a$。如果有行數或列數超出超出范圍的,要記得忽略掉。
余數 $r$ 的處理則比較復雜。這 $r$ 個點一般情況下應當像圖 1 一樣添加到最后一列的后面,因為上面 $a$ 的范圍保證了一般情況下 $a \le b$,多余的點作為新一列添加顯然能組成更多的長方形。多出來的長方形數量應該是以 $r$ 中的石子作為右下角的長方形數量,或者看成 $(b + 1) \times r$ 的石子包含的長方形數量減去 $b \times r$ 的石子包含的長方形數量(要除去重復的長方形數量)。結果為 $\frac{{br(r - 1)}}{2},r \ge 2$。
然后就要考慮不一般的情況了,一種是 $r < 2$,這時無論如何都不能組成長方形,無視它就好;還有一種是 $b+1$ 可能會大於 $M$(此時 $b = M$),就必須將 $r$ 個石子作為新一行添加到最后一行的后面,這時是可以保證 $a < N$ 的,因為 $K < N \times M$;最后一種情況是 $a > b$,這種情況只會出現在 $a = \left\lceil {\sqrt K } \right\rceil$ 時,這時也應當將 $r$ 個石子添加為新一行,但要保證 $i + 1 \le n$。
對了,還有最后一點:如果要過大數據的話,里面的 $a, b$ 都要用 long long 的,數據的最大值大概是 $30000^4$,還在 long long 的范圍內。
代碼
#include <stdio.h> #include <math.h> // 計算 a*b 的石子中包含的長方形數 long long calRects(long long a, long long b) { return a*b*(a-1)*(b-1)/4; } // 計算剩余的 r 個石子可以包含的長方形個數 long long calExtRects(long long t, long long r) { return t*r*(r-1)/2; } int main() { int T, n, m, k; scanf("%d ", &T); for (int caseIdx = 1;caseIdx <= T;caseIdx++) { scanf("%d%d%d", &n, &m, &k); // 保證 n <= m if (n > m) { int t = n; n = m; m = t; } long long max = 0; int maxa = (int)ceil(sqrt((double)k)); if (maxa > n) maxa = n; for (int a = 2;a <= maxa;a++){ int r = k % a; int b = (k - r) / a; if (b > m) continue; long long cnt = calRects(a, b); if (r >= 2) { // 添加剩余的 r 個石子為新一列 int t = b; if (b == m || (a < n && a > b && r < b)) { // 需要將石子添加為新一行的情況 t = a; } cnt += calExtRects(t, r); } if (cnt > max) max = cnt; } printf("Case #%d: %lld\n", caseIdx, max); } return 0; }