zoukankan      html  css  js  c++  java
  • 「ZJOI2007」棋盘制作

    在一个$N imes M$的$0 1$矩阵中,求面积最大的相邻位置数字不同的矩形和正方形。

    题目链接:BZOJ1057

    乍一看,也许暴力可以解决问题,可以暴力的枚举所取图形的长和宽,然后再暴力的枚举。

    但是这样的时间复杂度高达$O(n^2 m^2)$,肯定行不通,而且很难写。

    这时候,我们引入“悬线法”。

     对于每个位置,我们预处理出此节点向上方最长能够延申的合法长度$left$。

    再用另一个数组预处理出此节点向下方最长能够延申的合法长度$right$。

    然后再横向处理矩阵,求出每个点的横向长度合法最长值,用一条线连结。

    称为「悬线」。求出悬线向上向下能延伸到的最远位置,则悬线上下移动的轨迹为一个合法子矩阵,

    且这个子矩阵的上边、左边、下边不能延伸。如果它的右边可以延伸,则在接下来的枚举中,延伸得到的矩阵可以被枚举到,否则它就是一个极大合法子矩阵。

    时间复杂度$O(nm)$

    代码如下

    #include <stdio.h>
    #include <algorithm>
    int n, m;
    int qrt(int x)
    {
        return x * x;
    }
    int pe[2005][2005], b[2005][2005], c[2005][2005];
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                scanf("%d", &pe[i][j]);
            }
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (j == 1 || pe[i][j] == pe[i][j - 1])
                    b[i][j] = 1;
                else
                {
                    b[i][j] = b[i][j - 1] + 1;
                }
            }
            for (int j = m; j >= 1; j--)
            {
                if (j == m || pe[i][j] == pe[i][j + 1])
                    c[i][j] = 1;
                else
                {
                    c[i][j] = c[i][j + 1] + 1;
                }
            }
        }
        int sq_max = 0, rec_max = 0;
        for (int j = 1; j <= m; j++)
        {
            int up = 0, left = 0, right = 0;
            for (int i = 1; i <= n; i++)
            {
                if (i == 1 || pe[i][j] == pe[i - 1][j])
                {
                    up = 1;
                    left = b[i][j];
                    right = c[i][j];
                }
                else
                {
                    up++;
                    left = std::min(left, b[i][j]);
                    right = std::min(right, c[i][j]);
                }
                rec_max = std::max(rec_max, up * (left + right - 1));
                sq_max = std::max(sq_max, qrt(std::min(left + right - 1, up)));
            }
        }
        printf("%d
    %d
    ", sq_max, rec_max);
    
        return 0;
    }
    人十我百 人百我万
  • 相关阅读:
    每周总结8.18
    每周总结7.28
    每周总结8.25
    每周总结7.21
    每周总结8.11
    每周总结8.4
    大道至简 读后感
    递归进行回文的判断
    课后作业1
    GoodBlogs Websites
  • 原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/11235667.html
Copyright © 2011-2022 走看看