• 刷题记录【ZJOI2007棋盘制作】二维DP,悬线法。。。


    https://www.luogu.org/problemnew/show/P1169
    题目描述
    国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8×88×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。
    而我们的主人公小Q,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。
    小Q找到了一张由N×MN imes M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。
    不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。
    于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?
    输入输出格式
    输入格式:

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

    包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。
    第一次接触这种类型的二维dp
    因为过于水不得不在阅读过下面这篇论文之后才AC掉
    https://blog.csdn.net/clover_hxy/article/details/50532289
    自闭了
    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    #define N 2005 
    #define ll long long
    #define right wrong
    #define left niconico
    int f[N][N],height[N][N],left[N][N],right[N][N],n,m,ans1,ans2,map[N][N];
    inline int p2(int x){return x*x;}
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1 ; i <= n ; i ++)
        { 
        for(int j = 1 ; j <= m ; j ++)
        {
            scanf("%d",&map[i][j]);
            left[i][j]=right[i][j]=j; 
            height[i][j]=1;  
        }
        }
        for(int i = 0; i <= n; i ++ )map[i][0]=-1;
        for(int j = 1; j <= m; j ++ )map[0][j]=-1;
        for(int i = 1; i <= n ; i ++)
        for(int j = 1; j <= m ; j ++)
        {
            if(map[i][j]+map[i][j-1]==1)left[i][j]=left[i][j-1];
        }
        for(int i = 1; i <= n ; i ++)
        for(int j = m; j >= 1 ; j --)
        {
            if(map[i][j]+map[i][j-1]==1)right[i][j-1]=right[i][j];
        }
        for(int i = 1 ; i <= n; i ++)
        for(int j = 1 ; j <= m; j ++)
        {
            if(map[i][j]+map[i-1][j]==1)
            {
                height[i][j]=height[i-1][j]+1;
                left[i][j]=max(left[i][j],left[i-1][j]);
                right[i][j]=min(right[i][j],right[i-1][j]);
            }
        }
        for(int i = 1; i <= n ; i ++)
        for(int j = 1 ; j <= m ; j ++)
        {
            ans1 = max(ans1,height[i][j]*(right[i][j]-left[i][j]+1));
            ans2 = max(ans2,p2(min(height[i][j],right[i][j]-left[i][j]+1)));
        }
        printf("%d
    %d",ans2,ans1);
    }
    

    最后想一想还是写一下思路吧
    首先,悬线法的本质是枚举每一个点为下端点的最大悬线,将悬线左右移动,所能移动的最长区间长度即为最大矩形的一边长。而悬线的长即为另一边长。
    由于悬线移动的时间复杂度为O(n)O(n),所以不满足本题要求。为了能在O(1)时间内求出最大区间长,我们可以采用预处理的方法。
    用数组left[i][j],right[i][j]表示从i,j开始最靠左、右的符合要求区间终点。
    则先递推求出长度为1的悬线对应的left[i][j],right[i][j],
    再按照left[i][j]=max(left[i][j],left[i1][j])left[i][j]=max(left[i][j],left[i-1][j])right[i][j]=min(right[i][j],right[i1][j])right[i][j]=min(right[i][j],right[i-1][j])
    进行递推
    最后枚举所有的i,j
    则I,j对应的矩形面积为height[i][j]×(right[i][j]left[i][j]+1)height[i][j] imes(right[i][j]-left[i][j]+1)正方形面积为
    min(height[i][j],right[i][j]left[i][j]+1)2min(height[i][j],right[i][j]-left[i][j]+1)^2

  • 相关阅读:
    .NET 异步详解
    spring batch简介
    Nginx 配置文件介绍
    局域网内组播
    qt自定义信号函数的那些坑
    传输文件到远程服务器
    vim复制指定行
    腾讯云获取公网ip
    ifconfig添加或删除ip
    程序中tar压缩文件
  • 原文地址:https://www.cnblogs.com/akonoh/p/10216761.html
Copyright © 2020-2023  润新知