悬线法(棋盘制作+玉蟾宫

玉蟾宫
棋盘制作
OIwiki讲解

这是某dalao用长度来求解的
//悬线法 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 2005
using namespace std;
int n,m,a[maxn][maxn],ans1,ans2;
int h[maxn][maxn],r[maxn][maxn],l[maxn][maxn];
 
inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}
 
void pre(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){ 
            if(a[i][j]^a[i][j-1]) l[i][j]=l[i][j-1]+1;
            if(a[i][m-j+1]^a[i][m-j+2]) r[i][m-j+1]=r[i][m-j+2]+1;
            if(a[i][j]^a[i-1][j]) h[i][j]=h[i-1][j]+1;
        } 
}
 
int main(){
    n=rd(); m=rd();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[i][j]=rd();
    pre();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(h[i][j]){
                l[i][j]=min(l[i][j],l[i-1][j]);
                r[i][j]=min(r[i][j],r[i-1][j]);
            }
            int tmp=min(h[i][j]+1,(l[i][j]+r[i][j]+1));
            ans1=max(ans1,tmp*tmp);
            ans2=max(ans2,(h[i][j]+1)*(l[i][j]+r[i][j]+1));
        }
    printf("%d\n%d",ans1,ans2);
    return 0;
}

这是把问题转换为求最大全0子矩阵或最大全1子矩阵的问题求解
方法是 ==改变棋盘把行数+列数为奇数或偶数的位置异或 ==

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N=2010, M=2010;
int n,m;
int a[N][M],ri[N][M];
int ans1,ans2;
int topl[M],topr[M],up[M];

inline void getans(){
    memset(up,0,sizeof(up));
    int i,j,nowl,nowr,lin;
    for(j=1; j<=m; ++j){ topl[j]=1; topr[j]=m; }
    for(i=1; i<=n; ++i){    // 一行一行地刷新帘子  
        nowl=0; nowr=m+1;
        for(j=1; j<=m; ++j){    // 从左到右更新左端点  
            if(a[i][j]){
                up[j]=0;
                topl[j]=1;
                nowl = j;
            }else{
                up[j]++;
                topl[j] = max(topl[j], nowl+1);
            }
        }
        for(j=m; j>=1; --j){    // 从右到左更新右端点  
            if(a[i][j]){
                topr[j]=m;
                nowr=j;
            }else{
                topr[j] = min(topr[j], nowr-1);
                // 更新答案  
                lin = min(up[j], topr[j]-topl[j]+1);
                ans1 = max(ans1, lin*lin);
                ans2 = max(ans2, up[j]*(topr[j]-topl[j]+1));
            }
        }
    }
}

int main(){
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=1; i<=n; ++i){
        for(j=1; j<=m; ++j){
            scanf("%d",&a[i][j]);
        }
    }
    // 改变棋盘,把行数+列数为奇数或偶数的位置异或  多谢 dcx%dl 提醒 
    for(i=1; i<=n; ++i){
        for(j=1; j<=m; ++j){
            if((i+j)&1) a[i][j]^=1; 
        }
    }
    // 求一下全 1 子矩阵,求一下全 0 子矩阵 
    getans();
    for(i=1; i<=n; ++i){
        for(j=1; j<=m; ++j){
            a[i][j] = !a[i][j];
        }
    }
    getans();
    printf("%d\n%d\n",ans1,ans2);
    return 0;
}

还是长度好用啊,位置对坐标有要求,我看不懂还是算了
以后就用长度吧

#include<iostream>
using namespace std;
const int N=2001;
int h[N][N],l[N][N],r[N][N],a[N][N];
int n,m;
int ans1,ans2;

void init(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]&a[i][j-1])l[i][j]=l[i][j-1]+1;//左边不能再拓展了长度为0,但是它的右边那一块可向左拓展长度为1
            if(a[i][m-j+1]&a[i][m-j+2])r[i][m-j+1]=r[i][m-j+2]+1;
            if(a[i][j]&a[i-1][j])h[i][j]=h[i-1][j]+1;
        }
    }
}

int main(){
    int cntF=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            char c;
            cin>>c;
            if(c=='F')a[i][j]=1,cntF++;
        }
        
    }
    init();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(h[i][j]){
                l[i][j]=min(l[i][j],l[i-1][j]);
                r[i][j]=min(r[i][j],r[i-1][j]);
                //如果下比上长,较短的上面那条边会约束你可拓展的长度
                //如果上比下长,较短的下面那条边会约束你可拓展的长度
            }
            //int temp=min(h[i][j]+1 , l[i][j]+r[i][j]+1);
            //ans1=max(ans1,temp*temp);
            ans2=max(ans2,(h[i][j]+1)*(l[i][j]+r[i][j]+1));
            
        }
    }
    if(cntF==0)cout<<0;
    else cout<<ans2*3;
    
}




在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值