GESP C++ 五级编程题

T1 黑白格

题意:

给定一个n * m 的01网格。求最小包含至少k个1的矩形面积。

方法1: 二维前缀和 O(n^4)

思路:

首先枚举出矩形的左上端点和右下端点,然后使用二维前缀和来对这个矩形中的1的个数进行计数。二维前缀和的使用方法就不在这里赘述。

代码:
#include<bits/stdc++.h>

using namespace std;
const int N = 110;
int  s[N][N];
char t[N][N];

int get(int x1, int y1, int x2, int y2){
	return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
int main(){
	int n, m, k;
	cin >> n >> m >> k;	
	for(int i = 1; i <= n; i ++)cin >> t[i] + 1;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			int a = t[i][j] - '0';
			s[i][j] = a + s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1];
		}	
	}
	int ans = 0;
	for(int x1 = 1; x1 <= n; x1 ++){
		for(int y1 = 1; y1 <= m; y1 ++){
			for(int x2 = x1; x2 <= n; x2 ++){
				for(int y2 = y1; y2 <= m; y2 ++){
					if(get(x1, y1, x2, y2) >= k){
						if(ans == 0){
							ans = (x2 - x1 + 1) * (y2 - y1 + 1);
						}	
						else 	ans = min(ans, (x2 - x1 + 1) * (y2 - y1 + 1));
					}
				}
			}
		}
	}
	cout << ans << endl;
	
	return 0;
}

方法2 : 一维前缀和+二分 O(n^3 * log(n))

思路:

首先确定这个矩形的左右边界,然后再枚举下边界,也就是矩形的四条边我们只暴力枚举三条,然后在下边界枚举的过程中,维护一个数组,这个数组中存储的是每次新加一行后的值,因为这个矩阵中只存在0和1,我们可以保证当从第一层枚举到第i层的时候,前i层的和一定是非递减的。所以就满足了二分查找上边界的要求。
我们在新增一行的时候采用一维前缀和获取这一行的和。

代码:
#include<bits/stdc++.h>

using namespace std;
const int N = 110;
int  s[N][N];
char t[N][N];

int main(){
	int n, m, k;
	cin >> n >> m >> k;	
	for(int i = 1; i <= n; i ++)cin >> t[i] + 1;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			int a = t[i][j] - '0';
			s[i][j] = s[i][j - 1] + a;
			// 主席要记录行前缀和,用来快速求出规定左右边界中一行的和
		}	
	}
	int ans = 0;
	for(int y1 = 1; y1 <= m; y1 ++){
		for(int y2 = y1; y2 <= m; y2 ++){
			int now = 0; // 存储前x2 行的和
			vector<int> num;// 用来存储每添加一行后的前缀和的数据 
			for(int x2 = 1; x2 <= n; x2 ++){
				now += s[x2][y2] - s[x2][y1 - 1];
				//新增一行
				num.push_back(now);
				//添加到数组中
				if(now >= k){
					if(ans == 0)ans = x2 * (y2 - y1 + 1);
					else ans = min(ans,x2 * (y2 - y1 + 1));
					int l = 0, r = x2 - 1;
			//二分的原理:找到num中最大的数满足 now- num[]大于等于k
			//因为是找到最晚出现 所以要用下面这个二分模板
					while(l < r){
						int mid = l + r + 1 >> 1;
						if(now - num[mid] >= k)l = mid;
						else r = mid - 1;
					}
		//这里有一个细节 这个l代表的是数组中的位置,l+1才是他的真实行
		//但如果我在第l+1行满足条件,那么我的结果行应该是l+2.
		// 可以参照具体样例进行理解,如果我now - 第一行的才大于等于k。那么这个矩阵范围应该是 第二行和第五行。	
		// 所以他的宽应该是 x2 - (l + 2) + 1) 也就是 x2 - l - 1;
					if(now - num[l] >= k){
						if(ans == 0) ans = (y2 - y1 + 1) * (x2 - l - 1);
						ans = min(ans, (y2 - y1 + 1) * (x2 - l -1 ));
					}
				}
			}
		}
	}
	cout << ans << endl;
	
	return 0;
} 	
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值