• 【洛谷】P1169棋盘制作(悬线法)


    题目链接

    题目描述

    国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。

    而我们的主人公小Q,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。

    小Q找到了一张由N×M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。

    不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。

    于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?

    输入输出格式

    输入格式:

    包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的NN行包含一个N ×M的01矩阵,表示这张矩形纸片的颜色(0表示白色,1表示黑色)。

    输出格式:

    包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。

    输入输出样例

    输入样例#1: 复制

    3 3
    1 0 1
    0 1 0
    1 0 0
    

    输出样例#1: 复制

    4
    6
    

    说明

    对于20%的数据,N, M ≤ 80N,M≤80

    对于40%的数据,N, M ≤ 400N,M≤400

    对于100%的数据,N, M ≤ 2000N,M≤2000

    悬线法可在O(m*n)时间内求解满足条件的最大子矩阵,效率比一般的枚举法高得多。

    详情可参考:

    ACM算法:悬线法

    浅谈用极大化思想解决最大子矩阵问题

    合法的棋盘可看做是一条悬线(竖着的)左右平移得来的,因此只要求出悬线的高H,悬线能平移到最左的位置L,悬线能平移到最右的位置R,就能算出面积H*(L-R+1),矩形棋盘在里面求最大值即可,正方形棋盘先求出边长s=min(L-R+1,H),在s*s里面取最大值即可。

    设H[i][j]为底部端点是(i,j)的悬线的高度,L[i][j]为底部端点是(i,j)的悬线能平移到最左的位置,R[i][j]为底部端点是(i,j)的悬线能平移到最右的位置。

    (图片来源于https://blog.csdn.net/clover_hxy/article/details/50532289?locationNum=1&fps=1

    可知递推式为

    R[i][j]=min(R[i][j],R[i-1][j])

    L[i][j]=max(L[i][j],L[i-1][j])

    AC代码:

     1 #include<iostream>
     2 #include<sstream>
     3 #include<algorithm>
     4 #include<string>
     5 #include<cstring>
     6 #include<iomanip>
     7 #include<vector>
     8 #include<cmath>
     9 #include<ctime>
    10 #include<stack>
    11 #include<queue>
    12 #define e 2.71828182
    13 #define Pi 3.141592654
    14 using namespace std;
    15 int N,M,cb[2018][2018],H[2018][2018],L[2018][2018],R[2018][2018];
    16 int main()
    17 {
    18     cin>>N>>M;
    19     for(int i=1;i<=N;i++)
    20         for(int j=1;j<=M;j++)
    21             cin>>cb[i][j];
    22     //这部分求的是以(i,j)为底部端点的悬线的长度
    23     for(int j=1;j<=M;j++) H[1][j]=1;
    24     for(int i=2;i<=N;i++)
    25         for(int j=1;j<=M;j++)
    26             if(cb[i][j]^cb[i-1][j]) H[i][j]=H[i-1][j]+1;//cb[i][j]与cb[i-1][j]棋子一黑一白 
    27             else H[i][j]=1;
    28     //预处理,这部分求的是(i,j)能平移到最左的位置,并非是以(i,j)为底部端点的悬线能平移到最左的位置 
    29     for(int i=1;i<=N;i++) L[i][1]=1;
    30     for(int i=1;i<=N;i++)
    31         for(int j=2;j<=M;j++)
    32             if(cb[i][j]^cb[i][j-1]) L[i][j]=L[i][j-1];//cb[i][j]与cb[i][j-1]棋子一黑一白
    33             else L[i][j]=j;
    34     //预处理,这部分求的是(i,j)能平移到最右的位置,并非是以(i,j)为底部端点的悬线能平移到最右的位置 
    35     for(int i=1;i<=N;i++) R[i][M]=M;
    36     for(int i=1;i<=N;i++)
    37         for(int j=M-1;j>=1;j--)
    38             if(cb[i][j]^cb[i][j+1]) R[i][j]=R[i][j+1];//cb[i][j]与cb[i][j+1]棋子一黑一白
    39             else R[i][j]=j;
    40             
    41     int ans1=0,ans2=0;//最大正方形棋盘,最大矩形棋盘 
    42     for(int i=1;i<=N;i++)
    43     {
    44         for(int j=1;j<=M;j++)
    45         {
    46             if(i>1&&(cb[i][j]^cb[i-1][j]))//递推 
    47                 R[i][j]=min(R[i][j],R[i-1][j]),L[i][j]=max(L[i][j],L[i-1][j]);
    48             int l=R[i][j]-L[i][j]+1;
    49             int w=min(l,H[i][j]);//正方形的边长取l和H的较小值 
    50             ans1=max(ans1,w*w);
    51             ans2=max(ans2,l*H[i][j]);
    52         }
    53     }
    54     
    55     cout<<ans1<<endl<<ans2;
    56 }
    57  
    View Code
  • 相关阅读:
    一些数据集
    经典的图像匹配算法----SIFT
    LDA处理文档主题分布代码
    Dirichlet Process
    主题模型-LDA浅析
    matplotlib —— 添加文本信息(text)
    xgboost原理及应用
    XGBoost参数调优
    MySQL中实现Oracle里面 rank()over ( PARTITION BY ORDER BY) 分类分组功能
    4.12 省选模拟赛 LCA on tree 树链剖分 树状数组 分析答案变化量
  • 原文地址:https://www.cnblogs.com/wangzhebufangqi/p/12796132.html
Copyright © 2020-2023  润新知