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)

posted @ 2022-12-28 20:45  XiCen  阅读(45)  评论(0)    收藏  举报