• P1169 [ZJOI2007]棋盘制作


    题目描述

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

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

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

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

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

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1: 
    3 3
    1 0 1
    0 1 0
    1 0 0
    
    输出样例#1: 
    4
    6
    

    说明

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

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

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

    Solution:

      本题dp。

      第一问是常规dp,第二问奇偶分出黑白点,再分别对黑点取反求下最大的全0或全1矩形,然后对全图取反再求一次取max就好了。

      这里主要安利一个很骚的做法,据说叫做悬线法。

      思路很简单,开三个数组:$lf[i][j]$表示$(i,j)$点往左最远的合法位置,同理$rf[i][j]$表示往右的最远合法位置、$up[i][j]$表示往上的最远合法位置,这些直接$n^2$预处理就好了。

      然后就是枚举每个位置,求出合法的长和宽,更新答案就好了(实现时的细节就是把$up[i][j]$在枚举位置的时候求,并把其含义改为合法宽度,对于每个宽度都应该求下合法面积)。

    代码:

    /*Code by 520 -- 9.20*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    const int N=2005;
    int n,m,mp[N][N],lf[N][N],rf[N][N],up[N][N];
    int ans1,ans2;
    
    int main(){
        scanf("%d%d",&n,&m);
        For(i,1,n) For(j,1,m) scanf("%d",&mp[i][j]),lf[i][j]=rf[i][j]=j,up[i][j]=1;
        For(i,1,n) For(j,2,m) if(mp[i][j]^mp[i][j-1]) lf[i][j]=lf[i][j-1];
        For(i,1,n) Bor(j,1,m-1) if(mp[i][j]^mp[i][j+1]) rf[i][j]=rf[i][j+1];
        For(i,1,n) For(j,1,m) {
            if(i>1&&(mp[i][j]^mp[i-1][j])){
                lf[i][j]=max(lf[i][j],lf[i-1][j]);
                rf[i][j]=min(rf[i][j],rf[i-1][j]);
                up[i][j]=up[i-1][j]+1;
            }
            int c=rf[i][j]-lf[i][j]+1;
            int r=min(c,up[i][j]);
            ans1=max(ans1,r*r),ans2=max(ans2,c*up[i][j]);
        }
        cout<<ans1<<'
    '<<ans2;
        return 0;
    }
  • 相关阅读:
    Euclid's Game (简单博弈)
    “科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛(部分题解)
    牛客小白月赛4 (B 博弈论)
    博弈--尼姆博弈
    C# .net中获取台式电脑中串口设备的名称
    打印出C# 中float ,double 在内存中的存放形式
    VS2010 C++ 创建COM组件
    .net 中两个日期算经过的月份数
    一种计算MD5的实现方法
    将文件从程序集中复原
  • 原文地址:https://www.cnblogs.com/five20/p/9688811.html
Copyright © 2020-2023  润新知