时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
在 N 条水平线与 M 条竖直线构成的网格中,放 K 枚石子,每个石子都只能放在网格的交叉点上。问在最优的摆放方式下,最多能找到多少四边平行于坐标轴的长方形,它的四个角上都恰好放着一枚石子。
输入
输入文件包含多组测试数据。
第一行,给出一个整数T,为数据组数。接下来依次给出每组测试数据。
每组数据为三个用空格隔开的整数 N,M,K。
1 ≤ T ≤ 100
0 ≤ K ≤ N * M
小数据:0 < N, M ≤ 30
大数据:0 < N, M ≤ 30000
输出
对于每组测试数据,输出一行"Case #X: Y",其中X表示测试数据编号,Y表示最多能找到的符合条件的长方形数量。所有数据按读入顺序从1开始编号。
样例输入
3
3 3 8
4 5 13
7 14 86
样例输出
Case #1: 5
Case #2: 18
Case #3: 1398
先看一道微软2013校园招聘笔试题:
How many rectangles you can find from 3*4 grid?
A. 18
B. 20
C. 40
D. 60
E. None of above is correct
从纵向和横向的各条边中,每各选出两条边就会有一个由四个交点组成的矩形,所以答案是C32×C42 = 60.
回归到本题,可以枚举纵向或横向的边数:2~N(或M),对于多余的石子,加入到行或列中(前提是行或列没有达到最大值)。
算法:
假如现在有m行n列,k=K-m×n,此时可得到的最大矩形数目是:Cm2×Cn2 + Ck2 × max(m, n), m <M && n < N.
枚举m或n即可。
优化:
事实上,不需要枚举这么多次,假设N < M,只要枚举2~min(N, K½)即可.
#include<cstdio> #include<cmath> using namespace std; void swap(int& a, int& b) { int t = a; a = b; b = t; } int main() { int T, N, M, K; scanf("%d", &T); int m, n, tmp, ans; for(int i = 1; i <= T; ++i) { ans = 0; scanf("%d%d%d", &N, &M, &K); if(N > M)//使M最大 swap(N, M); tmp = sqrt(K); n = N > tmp ? tmp : N; m = M; while(n * m > K) --m; while(n >= 2 && m <= M) { tmp = (n * m * (n - 1) * (m - 1)) >> 2; int k = K - m * n; if(m < M) tmp += (k * (k - 1) * m) >> 1; else tmp += (k * (k - 1) * n) >> 1; ans = tmp > ans ? tmp : ans; --n; m = K / n; } printf("Case #%d: %d\n", i, ans); } return 0; }