• BZOJ1057: [ZJOI2007]棋盘制作


    BZOJ1057: [ZJOI2007]棋盘制作

    Description

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

    Input

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

    Output

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

    Sample Input

    3 3
    1 0 1
    0 1 0
    1 0 0

    Sample Output

    4
    6

    HINT

    N, M ≤ 2000


    题解Here!

    据说这个题可以单调栈做但是本蒟蒻不会啊。。。
    最近$DP$写多了脑子都瓦特了。。。
    设:
    $Left[i][j]$:代表从$(i,j)$能到达的最左位置
    $Right[i][j]$:代表从$(i,j)$能到达的最右位置
    $Up[i][j]$:代表从$(i,j)$向上扩展最长长度
    因为$Up[i][j]$的定义,$Up$数组代表向上扩展最长长度,所以需要考虑上一层的情况。
    于是状态转移方程:

    $$Left[i][j]=maxleft{egin{array}{}Left[i][j]\Left[i-1][j]end{array} ight.$$
    $$Right[i][j]=minleft{egin{array}{}Right[i][j]\Right[i-1][j]end{array} ight.$$

    然后分正方形和长方形两种情况即可。

    据说这个方法叫悬线法。。。
    定义:用一条线(横竖貌似都行)左右移动直到不满足约束条件或者到达边界。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 2010
    using namespace std;
    int n,m,ans1=0,ans2=0;
    int chess[MAXN][MAXN],Left[MAXN][MAXN],Right[MAXN][MAXN],Up[MAXN][MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    void work(){
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++){
    		if(i>1&&chess[i][j]!=chess[i-1][j]){
    			Left[i][j]=max(Left[i][j],Left[i-1][j]);
    			Right[i][j]=min(Right[i][j],Right[i-1][j]);
    			Up[i][j]=Up[i-1][j]+1;
    		}
    		int x=Right[i][j]-Left[i][j]+1,y=min(x,Up[i][j]);
    		ans1=max(ans1,y*y);
    		ans2=max(ans2,x*Up[i][j]);
    	}
    	printf("%d
    %d
    ",ans1,ans2);
    }
    void init(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++){
    		chess[i][j]=read();
    		Left[i][j]=Right[i][j]=j;
    		Up[i][j]=1;
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=2;j<=m;j++)if(chess[i][j]!=chess[i][j-1])Left[i][j]=Left[i][j-1];
    		for(int j=m-1;j>=1;j--)if(chess[i][j]!=chess[i][j+1])Right[i][j]=Right[i][j+1];
    	}
    }
    int main(){
    	init();
    	work();
        return 0;
    }
    
  • 相关阅读:
    win8 需要管理员权限才能删除此应用程序
    windows 8 解决端口(COM和LPT)问题:Prolific USBtoSerial Comm Port(COM4)驱动异常的问题
    sqlite 获取数据库中的所有表
    ListView simpleAdapter的基本使用
    一根长度为133米的材料需要截成长度为19米和23米的短料,求两种短料各截多少根时剩余的材料最少?
    打印输出九九乘法表
    写一个函数,输入一个十六进制,输出相应的十进制数。
    求3100的素数!
    从键盘输入若干个同学的成绩,统计并输出最高成绩 最低成绩 平均分,当输入负数时结束输入
    编写两个函数,分别求由键盘输入两个整数的最大公约数和最小公倍数。用主函数调用这两个函数,并输出结果
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9846738.html
Copyright © 2020-2023  润新知