P1169 [ZJOI2007]棋盘制作

来源:互联网 发布:苹果远程连接软件 编辑:程序博客网 时间:2024/06/06 07:19
//P1169 [ZJOI2007]棋盘制作

//http://blog.csdn.net/Never_See/article/details/71307772此文图文并茂,介绍得不错,摘抄如下:

我们可以先转化一下
为了方便理解这里用一个比较特殊的图
这里写图片描述
因为是要求相邻的不同
那么这样看
这里写图片描述
我们把红色区域所有的数值0变为1,1变为0
就变成了这个样子
这里写图片描述
这时候我们原本是需要相邻点值不同,但现在可以直接是当做相邻的点相同
可以这样做是与我们选取改变的点有关的
我们选取改变的点都是尽可能相邻且不缺少的
或者从左上往右下看,看做从左上角的点出发,每一个与其相邻的点都要不同,那就把它们的权值改变一下,然后再从这些相邻的点出发,这些点的集合称为S,那么所有与S相邻的点权值都要不同,但因为S里面的点权都已经改变过了所以这些新的相邻点就不用改变,然后再看这些新的相邻点,更新的相邻点又需要改变……最后就变成了这样一个图

所以问题就变成了求有障碍点的极大化子矩阵

第一问可以DP
f[i][j]表示以点(i,j)为正方形矩阵的右下角的点时的最大边长

if( A[i][j]!=A[i-1][j-1] )f[i][j] = 1;else f[i][j] = min( min( l[i][j],Up[i][j] ),f[i-1][j-1]+1 );
  • 1
  • 2
  • 1
  • 2

Up[i][j]表示(i,j)往上有多少个连续相同,l[i][j]表示(i,j)往左有多少个连续相同
A[i][j]表示(i,j)的值

第二问用悬线法
同样Up[i][j]表示(i,j)往上有多少个连续相同,A[i][j]表示(i,j)的值
对于(i,j)Up[i1][j]>=Up[i][j](i1,j)(i,j)(i,j)Up
既然知道了以(i,j)为底边一点的最大子矩阵的左端点右端点和上端点那么我们可以求出所有这样的子矩阵的面积然后取最大就可以了

当然可以用单调栈或其他方法
这里不加阐述


//P1169 [ZJOI2007]棋盘制作
//http://blog.csdn.net/Never_See/article/details/71307772此文图文并茂,介绍得不错
//悬线法 https://wenku.baidu.com/view/0d787294dd88d0d233d46a96.html 此文介绍得不错,图文并茂,悬线法有了初步了解
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1169 作者: Tgotp 更新时间: 2017-06-24 14:12 代码写得不错
//按自己理解,编码,样例通过,提交WA //1 此处写成 scanf("%d%d",&a[i][j]);造成m=0,查了会
//造了一组数据,找到了问题所在
//输入
//3 3
//1 1 1
//1 0 1
//1 1 1
//输出
//1 3
//再次提交WA //2 漏了该功能
//再次修改,提交AC //3 漏了该句 2017-9-11 21:40
#include <stdio.h>
#define maxn 2010
int a[maxn][maxn],up[maxn][maxn],left[maxn][maxn],right[maxn][maxn];//up改点到上端的最大距离 left改点到左端的最大距离 right改点到右端的最大距离
int min(int a,int b){
    return a>b?b:a;
}
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int n,m,i,j,ans1=0,ans2=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%d",&a[i][j]);//1 此处写成 scanf("%d%d",&a[i][j]);造成m=0,查了会
    for(j=1;j<=m;j++)//初始化
        up[1][j]=1;
    for(i=1;i<=n;i++)
        left[i][1]=1;
    for(i=1;i<=n;i++)
        right[i][m]=1;
    for(i=2;i<=n;i++)
        for(j=1;j<=m;j++)
            if(a[i][j]!=a[i-1][j])
                up[i][j]=up[i-1][j]+1;
            else
                up[i][j]=1;
    for(j=2;j<=m;j++)
        for(i=1;i<=n;i++)
            if(a[i][j]!=a[i][j-1])
                left[i][j]=left[i][j-1]+1;
            else
                left[i][j]=1;
    for(j=m-1;j>=1;j--)
        for(i=1;i<=n;i++)
            if(a[i][j]!=a[i][j+1])
                right[i][j]=right[i][j+1]+1;
            else
                right[i][j]=1;
    for(i=2;i<=n;i++)//2 漏了该功能
        for(j=1;j<=m;j++)
            if(a[i][j]!=a[i-1][j]){//3 漏了该句
                left[i][j]=min(left[i][j],left[i-1][j]);
                right[i][j]=min(right[i][j],right[i-1][j]);
            }
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            ans1=max(ans1,min(up[i][j],left[i][j]+right[i][j]-1));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            ans2=max(ans2,up[i][j]*(left[i][j]+right[i][j]-1));
    printf("%d\n",ans1*ans1);
    printf("%d\n",ans2);
    return 0;
}