Codeforces Round #841 (Div. 2) and Divide by Zero 2022------D. Valiant's New Map
题目链接:https://codeforces.com/contest/1731/problem/D

题目的大致意思是:
给你一个n*m的矩阵,问你最大的L(L<=n,L<=m),使得矩阵中存在LxL的的子矩阵,在这子矩阵内的每个数都大于大于等于L;
这题有俩个思路;
一个是暴力:
我们先来思考,如果L = 1的时候,显然是每个点上的值,当L = 2的时候:
1 2 3
4 5 6
7 8 9
这个矩阵,当L=2的时候,以(1,1)为左上角的长度为2的方阵,是1 2
4 5
在以(1,1)为左上角长度为2的方阵,他的最小值是1;
以(1,2)为左上角长度为2的方阵,他的最小值是2;
以(2,1)为左上角长度为2的方阵,他的最小值是4;
以(2,2)为左上角长度为2的方阵,他的最小值是5;
我们思考,在已知以(i,j)为左上角的长度为L方阵的的最小值的时候,如何知道以(i,j)为左上角的长度为L+1方阵的最小值;
我们通过观察上面 的例子可以看出,以长度为2的(1,1),(1,2),(2,1),(2,2)为左上角的方阵,刚好把以(1,1)为左上角长度为3的方阵给刚好覆盖了;
所以,如果知道知道了nums[L][i][j],nums[L][i][j+1],nums[L][i+1][j],nums[L][i+1][j+1],(第一维表示方阵的长度,后面俩维表示坐标;);
那么nums[L+1][i][j] = min(nums[L][i][j],nums[L][i+1][j],nums[L][i][j+1],nums[L][i+1][j+1]);
于是,我们就知道了方阵在长度为LxL内的最小值,我们可以从1一直跑到1000,然后每次得到的最小值与L进行比较即可;
因为n*m<=1e6,所以可以知道L一定小于等于1000,那么我们的时间复杂度为(1000*n*m)//ps:因为cf的测评机一秒大概可以跑1e9,所以我们这个暴力是刚刚好可以过的;
//用滚动数组来更新L,不然会MLE的捏;
代码如下:
#include<bits/stdc++.h> //#define int long long int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); int T; std::cin >> T; while (T--) { int n, m; std::cin >> n >> m; std::vector nums(2, std::vector(n + 1, std::vector<int>(m + 1, 0))); int mi = 1e18; for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)std::cin >> nums[0][i][j]; int ans = 1; for (int k = 2; k <= 1000; ++k) { for (int i = 1; i + k - 1 <= n; ++i) for (int j = 1; j + k - 1 <= m; ++j) { nums[!(k & 1)][i][j] = std::min({ nums[(k & 1)][i][j],nums[(k & 1)][i + 1][j],nums[(k & 1)][i][j + 1],nums[(k & 1)][i + 1][j + 1] }); if (nums[!(k & 1)][i][j] >= k)ans = k; } } std::cout << ans << "\n"; } return 0; }
一个是二分:
显然,因为要找最大值,那么他会在某个L上满足,然后在L+1上不满足,于是可以用二分来跑;
细节与思路见代码。时间复杂度O(n*m*log(1000));
代码如下:
#include<bits/stdc++.h> int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); int T; std::cin >> T; while (T--) { int n, m; std::cin >> n >> m; std::vector nums(n + 1, std::vector<int>(m + 1, 0)); for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)std::cin >> nums[i][j]; int L = 1, R = 1001; while (R - L > 1) { int MID = (R + L) / 2; bool Y = false; std::vector<int> cnt(m + 1, 0); //cnt[i]数组是有用来统计以i为结尾的,连续有op大于MID的行数 for (int i = 1; i <= n; ++i) { int op = 0;//op是用来统计连续大于MID的个数 for (int j = 1; j <= m; ++j) { op = (nums[i][j] >= MID ? op + 1 : 0); cnt[j] = (op >= MID ? cnt[j] + 1 : 0); //如果连续大于MID的个数大于等于MID,那么可以在cnt[i]上加一; //否则会变为0,因为不连续了 if (cnt[j] >= MID) { Y = true; goto END; } //如果cnt[j]大于等于MID,说明存在该长度的方阵 } }END:; if (Y)L = MID; else R = MID; } std::cout << L << "\n"; } return 0; }
这题感觉其实比C要简单来着,毕竟暴力都可以过(笑.jpg)

浙公网安备 33010602011771号