黑白格

题目描述

小杨有一个 n 行 m 列的网格图,其中每个格子要么是白色,要么是黑色。

小杨想知道至少包含 k 个黑色格子的最小子矩形包含了多少个格子。

输入格式

第一行包含三个正整数 n,m,k,含义如题面所示。

之后 n 行,每行⼀个长度为 m 的 01 串,代表网格图第 i 行格子的颜色,如果为 0,则对应格子为白色,否则为黑色。

输出格式

输出一个整数,代表至少包含 k 个黑色格子的最小子矩形包含格子的数量,如果不存在则输出 0。

输入输出样例

输入 #1

4 5 5
00000
01111
00011
00011

输出 #1

6

说明/提示

样例解释

对于样例 1,假设 (i,j) 代表第 i 行第 j 列,至少包含 5 个黑色格子的最小子矩形的四个顶点为 (2,4),(2,5),(4,4),(4,5),共包含 6 个格子。

数据范围

对于全部数据,保证有 1≤n,m≤100,1≤k≤n×m。

子任务编号得分n,m
120≤10
240n=1,1≤m≤100
340≤100

做法一:暴力

#include <iostream>
using namespace std;

int s[110][110];
int main()
{
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			char c;
			cin>>c;
			s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+(c=='1');
		}
	int maxn=2e9;
	for(int r1=1;r1<=n;r1++)
		for(int r2=r1;r2<=n;r2++)
			for(int c1=1;c1<=m;c1++)
				for(int c2=c1;c2<=m;c2++)
				{
					int area=(r2-r1+1)*(c2-c1+1);
					int b=s[r2][c2]-s[r1-1][c2]-s[r2][c1-1]+s[r1-1][c1-1];
					if(b>=k&&area<maxn)
						maxn=area;
				}
	cout<<(maxn<2e9?maxn:0);
	
	return 0;
}

搞一个二位前缀和暴力,打擂台,无了,但是O(n⁴),这道题数据小能过。

---------------------------------------------------------------------------------------------------------------------------------

做法二:二分 

#include <iostream>
using namespace std;

int n,m,k,r1,r2,s[110][110];
int f(int a,int b,int c,int d)
{
	return s[b][d]-s[a-1][d]-s[b][c-1]+s[a-1][c-1];
}
bool check(int mid)
{
	for(int l=1;l+mid-1<=m;l++)
	{
		int r=l+mid-1;
		int b=f(r1,r2,l,r);
		if(b>=k)
			return true;
	}
	return false;
}
int bs()
{
	int l=1,r=m;
	while(l<r)
	{
		int mid=(l+r)/2;
		if(check(mid))
			r=mid;
		else
			l=mid+1;
	}
	return l;
}
int main()
{
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			char c;
			cin>>c;
			s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+(c=='1');
		}
	int minx=2e9;
	for(r1=1;r1<=n;r1++)
		for(r2=r1;r2<=n;r2++)
		{
			if(f(r1,r2,1,m)<k)
				continue;
			int w=bs();
			int area=(r2-r1+1)*w;
			if(area<minx)
				minx=area;
		}
	cout<<(minx==2e9?0:minx);
	
	return 0;
}

做法:

        1.二层循环固定r1和r2。

        2.二分查找,找宽度(即c1和c2差)。

        3.check里枚举所有可能,有一个满足就return true。

        4.二层循环*二分*check,复杂度O(n³logn)。

细节:

        1.写一个f函数算二维区间和,简洁还能偷懒o(* ̄▽ ̄*)ブ

        2.由于是二分,必须保证两头至少一个是true,不然会出错,所以要提前判断这个r1和r2的最大区间够不够k个,不够continue。

### C++ 黑白算法实现 #### 基于前缀和的黑白问题解决方法 通过分析引用中的代码片段[^1] 和 [^2],可以得出一种基于前缀和的方法来高效计算满足条件的最小矩阵面积。这种方法的核心在于利用二维数组 `sum` 来存储累积和,从而快速查询任意子矩阵内的黑总数。 以下是完整的解决方案: ```cpp #include <bits/stdc++.h> using namespace std; int main() { int n, m, k; cin >> n >> m >> k; vector<vector<int>> sum(n + 1, vector<int>(m + 1, 0)); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { char c; cin >> c; sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + (c == '1' ? 1 : 0); } } int ans = INT32_MAX; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { for (int a = i; a <= n; ++a) { for (int b = j; b <= m; ++b) { int total = sum[a][b] - sum[a][j - 1] - sum[i - 1][b] + sum[i - 1][j - 1]; if (total >= k) { ans = min(ans, (a - i + 1) * (b - j + 1)); } } } } } cout << (ans == INT32_MAX ? 0 : ans); return 0; } ``` 此程序的时间复杂度为 \( O(N^4) \),但在实际应用中可以通过优化减少不必要的循环次数。 --- #### 并查集(Union-Find)解决最大连通区域问题 对于寻找最大家族大小的问题,可以采用并查集的思。该方法适用于判断哪些格子属于同一个家族,并统计每个家族的规模。 下面是具体的实现方式: ```cpp #include <bits/stdc++.h> using namespace std; class UnionFind { public: vector<int> parent; vector<int> rank_; vector<int> size_; UnionFind(int N) { parent.resize(N), rank_.resize(N, 0), size_.resize(N, 1); for (int i = 0; i < N; ++i) parent[i] = i; } int find_set(int x) { if (parent[x] != x) parent[x] = find_set(parent[x]); return parent[x]; } void union_set(int x, int y) { int fx = find_set(x), fy = find_set(y); if (fx == fy) return; if (rank_[fx] > rank_[fy]) swap(fx, fy); parent[fx] = fy; size_[fy] += size_[fx]; if (rank_[fx] == rank_[fy]) rank_[fy]++; } int get_size(int x) { return size_[find_set(x)]; } }; int main() { int n, m; cin >> n >> m; vector<string> grid(n); for (auto &row : grid) cin >> row; UnionFind uf(n * m); // 定义方向向量 vector<pair<int, int>> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (grid[i][j] == '1') { for (const auto &[dx, dy] : directions) { int ni = i + dx, nj = j + dy; if (ni >= 0 && ni < n && nj >= 0 && nj < m && grid[ni][nj] == '1') { uf.union_set(i * m + j, ni * m + nj); } } } } } int max_family = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (grid[i][j] == '1') { max_family = max(max_family, uf.get_size(i * m + j)); } } } cout << max_family; return 0; } ``` 这段代码实现了并查集结构用于处理连通分量的最大值问题。其核心逻辑是对每个黑色格子尝试与其上下左右四个方向上的邻居建立连接关系,最终得到最大的家族规模。 --- #### Harris角点检测的应用扩展 虽然Harris角点检测主要用于图像处理领域[^3],但它也可以间接应用于黑白问题。例如,在某些情况下,我们可以将黑白视为二值化后的灰度图,并借助OpenCV库完成进一步操作。不过这种做法通常超出题目范围,仅作为理论补充说明存在。 --- ### 总结 以上两种主要方法分别针对不同类型的黑白问题提供了高效的C++实现方案。前者适合求解特定条件下最优解;后者则专注于探索全局范围内最大连通域特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值