返回顶部

建造防风林

题目描述:给你一个\(n \times m\)的二维矩阵\(grid\),再给你一个整数\(k\),你可以从二维矩阵中任意选择\(k\)行出来,组合成新的二维矩阵,对于新的二维矩阵,其每一列的最大值的最小值最大是多少。

数据范围:\(1 \leq n \leq 10^5 , 1 \leq m \leq 5 , 1 \leq k \leq 5 , 1\leq grid_{i , j} \leq 10^9\)

思路:根据题意可以很明显的知道是二分答案,我们首先二分最大值,然后将原数组中小于最大值的置为\(0\),将大于等于最大值的置为\(1\),考虑到列数不超过\(5\),可以状压,将每一行压成一个不超过\(31(2^5 - 1)\)的整数,那么问题就变成在这\(n\)个数中选择\(k\)个数使得其或运算的值为\(2^m - 1\)是否可能。该问题显然有以下转移方程:

\[f_{i , j} = max(f_{i - 1 , j} , f_{i - 1 , j - 1} | val_i) \]

最后只需要判断\(f_{n , k} == 2^m - 1\)是否成立即可。该转移过程可以使用滚动数组优化空间。

空间复杂度:\(O(nm)\)

时间复杂度:\(O(n(m + k)log10^9)\)

参考代码:

#include<bits/stdc++.h>
using namespace std;

bool check(vector<int>& c, int n, int m, int k) {
	vector<vector<int>>f(2, vector<int>(k + 1, 0));
	int op = 0;
	for (int i = 0; i < n; ++i) {
		for (int j = 1; j <= k; ++j) {
			f[op][j] = max(f[op ^ 1][j], f[op ^ 1][j - 1] | c[i]);
		}
		op ^= 1;
	}
	return f[op ^ 1][k] == (1 << m) - 1;
}
void solve() {
	int n, m, k;
	cin >> n >> m >> k;
	vector<vector<int>>a(n, vector<int>(m, 0));
	vector<int>c(n);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[i][j];
		}
	}
	int lr = 1, rs = 1e9 + 7, res = 1;
	while (lr <= rs) {
		int mid = 1ll * (lr + rs) / 2;
		for (int i = 0; i < n; ++i) {
			c[i] = 0;
			for (int j = 0; j < m; ++j) {
				if (a[i][j] < mid) continue;
				c[i] |= (1 << j);
			}
		}
		if (check(c, n, m, k)) res = mid, lr = mid + 1;
		else rs = mid - 1;
	}
	cout << res << '\n';
	return;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T = 1;
	//cin >> T;
	while (T--) solve();
	return 0;
}

/*
7 5 3
5 4 6 9 8
8 5 3 8 4
4 5 8 7 4
4 10 5 7 1
8 1 3 7 7
2 6 6 3 4
10 1 6 8 2

4 3 2
4 2 7
5 1 6
4 3 3
1 2 5

3 4 2
3 4 2 1
2 2 5 3
1 2 4 1
*/
posted @ 2022-06-11 10:50  cherish-lgb  阅读(44)  评论(0编辑  收藏  举报