【codeforces1060C】【思维】【Maximum Subrectangle】

题目来源: http://codeforces.com/problemset/problem/1060/C
题目描述

现有两个长度分别为 n , m n, m n,m的数组 a , b a, b a,b,数组中的元素都为正数,令 C C C为一个 n × m n \times m n×m的矩阵,其中 C i , j = a i ⋅ b j C_{i, j} = a_i \cdot b_j Ci,j=aibj
现需要找 C C C的一个子矩阵,子矩阵的元素之和不超过 X X X的元素个数最多的子矩阵。需要找到最大的s,满足可找到这样的四个整数 x 1 , x 2 , y 1 , y 2 x_1, x_2, y_1, y_2 x1,x2,y1,y2满足 1 ≤ x 1 ≤ x 2 ≤ n , 1 ≤ y 1 ≤ y 2 ≤ m , ( x 2 − x 1 + 1 ) × ( y 2 − y 1 + 1 ) = s 1 \le x_1 \le x_2 \le n, 1 \le y_1 \le y_2 \le m, (x_2 - x_1 + 1) \times (y_2 - y_1 + 1) = s 1x1x2n,1y1y2m,(x2x1+1)×(y2y1+1)=s同时,

∑ i = x 1 x 2 ∑ j = y 1 y 2 c i , j ≤ X \sum_{i = x_1}^{x_2}\sum_{j = y_1}^{y_2}{c_{i,j} \le X} i=x1x2j=y1y2ci,jX.

输入格式

第一行包含两个整数 n , m ( 1 ≤ n , m ≤ 2000 ) n, m(1 \le n, m \le 2000) n,m(1n,m2000)。第二行包含 n n n个整数 a 1 , a 2 , . . . , a n ( 1 ≤ a i ≤ 2000 ) a_1, a_2, ..., a_n(1 \le a_i \le 2000) a1,a2,...,an(1ai2000)。第三行包含 m m m个整数 b 1 , b 2 , . . . , b m ( 1 ≤ b i ≤ 2000 ) b_1, b_2, ..., b_m(1 \le b_i \le 2000) b1,b2,...,bm(1bi2000)。第四行包含一个整数 X ( 1 ≤ X ≤ 2 ⋅ 1 0 9 ) X(1 \le X \le 2 \cdot 10^9) X(1X2109)

输出格式

如果能找到这样的四个整数 x 1 , x 2 , y 1 , y 2 x_1, x_2, y_1, y_2 x1,x2,y1,y2满足 1 ≤ x 1 ≤ x 2 ≤ n , 1 ≤ y 1 ≤ y 2 ≤ m , ∑ i = x 1 x 2 ∑ j = y 1 y 2 c i , j ≤ X 1 \le x_1 \le x_2 \le n, 1 \le y_1 \le y_2 \le m, \sum_{i = x_1}^{x_2}\sum_{j = y_1}^{y_2}{c_{i,j} \le X} 1x1x2n,1y1y2m,i=x1x2j=y1y2ci,jX。输出满足上述条件中最大的 ( x 2 − x 1 + 1 ) × ( y 2 − y 1 + 1 ) (x_2 - x_1 + 1) \times (y_2 - y_1 + 1) (x2x1+1)×(y2y1+1),否则输出 0 0 0

样例1输入

3 3
1 2 3 1 2 3
9

样例1输出

4

样例2输入

5 1
5 4 2 4 5 2
5

样例2输出

1

样例解释

第一组样例中选择的矩阵左上角坐标为 ( 1 , 1 ) (1, 1) (1,1)右下角坐标为 2 , 2 2, 2 2,2。第二组样例中选择的矩阵左上角坐标为 ( 1 , 3 ) (1, 3) (1,3)右下角坐标为 ( 1 , 3 ) (1, 3) (1,3)

分析

1.思考一个问题,矩阵中相同规模(矩阵的行数和列数)的子矩阵有很多个
那么对于同一个规模的子矩阵,是不是只需要关心它的最小值

[ 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 ] \left[ \begin{matrix} 1 & 2 & 3 & 4\\ 2 & 3 & 4 & 5 \\ 3 & 4 & 5 & 6 \\ 4 & 5 & 6 & 7 \end{matrix} \right] 1234234534564567

2.上述矩阵中若要求包含中间四个元素的子矩阵的加和,由题意可知: S = a 2 × b 2 + a 2 × b 3 + a 3 × b 2 + a 3 × b 3 = a 2 × ( b 2 + b 3 ) + a 3 × ( b 2 + b 3 ) = ( b 2 + b 3 ) × ( a 2 + a 3 ) S = a_2 \times b_2 + a_2 \times b_3 + a_3 \times b_2 + a_3 \times b_3 = a_2 \times (b_2 + b_3) + a_3 \times (b_2 + b_3) = (b_2 + b_3) \times (a_2 + a_3) S=a2×b2+a2×b3+a3×b2+a3×b3=a2×(b2+b3)+a3×(b2+b3)=(b2+b3)×(a2+a3) 所以如果知道行列上的区间,并求出区间和再相乘就可以得到子矩阵内元素的加和了。

结合1中结论,想要某个规模下如 k × l k \times l k×l 子矩阵的元素加和的最小值,又2中结论可知要求该规模下的子矩阵的元素加和等于 a a a 数组中对应区间加和 乘 b b b 数组对应区间加和。现只关心其最小值,所以只需关心 a a a 数组中对应区间长度加和的最小值 和 b b b 数组中对应区间长度加和的最小值。两者相乘,一定是该规模下子矩阵元素加和的最小值。

之后该题的做法就比较清晰了。要求 a , b a, b a,b数组对应区间长度的最小加和,首先要求已知区间的加和,可以用前缀和处理,所以先对 a , b a, b a,b数组做处理, 然后枚举区间长度,由于不同区间长度之间并没有关系,所以从小到大和从大到小都可。枚举所有区间长度,再枚举所有区间左端点,可以求出区间右端点,利用处理过的 a , b a, b a,b数组可以快速的求得区间和,记录对应区间长度下的加和最小值。最后嵌套循环枚举不同的区间长度,可遍历到所有规模的子矩阵,求出其加和最小值与X比较即可,记录满足条件的子矩阵的规模,即元素个数。

AC代码:
#include
#define N 2020
#define LL long long
using namespace std;
int n, m, ans;
LL x, a[N], b[N], c[N], d[N]; 
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) {
		scanf("%I64d", &a[i]);
		a[i] += a[i - 1];//顺便做前缀和处理
	}
	for(int i = 1; i <= m; ++i) {
		scanf("%I64d", &b[i]);
		b[i] += b[i - 1];
	}
	scanf("%I64d", &x);
	for(int l = 1; l <= n; ++l) {
		for(int i = 1; i <= n - l + 1; ++i) {
			int j = i + l - 1;
			c[l] = c[l] == 0 ? a[j] - a[i - 1] : min(c[l], a[j] - a[i - 1]);//枚举区间长度和区间左端点,记录对应区间长度下的加和最小值
		}
	}
	for(int l = 1; l <= m; ++l) {
		for(int i = 1; i <= m - l + 1; ++i) {
			int j = i + l - 1;
			d[l] = d[l] == 0 ? b[j] - b[i - 1] : min(d[l], b[j] - b[i - 1]);
		}
	}
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) {//枚举所有规模的子矩阵,记录满足条件下的子矩阵的最大规模
			LL tem = c[i] * d[j];
			if(x >= tem) 
				ans = max(ans, i * j);
		}
	}
	printf("%d\n", ans);
	return 0;
}

你可能感兴趣的:(思维,思维)