悬线法解决最大子矩形问题(玉蟾宫,巨大的牛棚,棋盘制作)——Java

本文通过三个实例介绍了一种名为悬线法的技术,该方法用于解决寻找最大矩形或正方形区域的问题。这些实例涵盖了不同的场景,如确定最大‘F’矩形土地面积、在无树区域构建最大正方形牛棚以及从黑白格子中找出最大正方形和矩形棋盘。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下三题都采用了悬线法的思想,套模板就行

题目描述
这片土地被分成 N\times MN×M 个格子,每个格子里写着 ‘R’ 或者 ‘F’,R 代表这块土地被赐予了 rainbow,F 代表这块土地被赐予了 freda。

现在 freda 要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着 ‘F’ 并且面积最大。

但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为 SS,它们每人给你 SS 两银子。

输入格式
第一行两个整数 NN,MM,表示矩形土地有 NN 行 MM 列。

接下来 NN 行,每行 MM 个用空格隔开的字符 ‘F’ 或 ‘R’,描述了矩形土地。

输出格式
输出一个整数,表示你能得到多少银子,即 (3\times \text{最大 ‘F’ 矩形土地面积}3×最大 ’F’ 矩形土地面积) 的值。


import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[][] map = new int[n+1][m+1];
        int[][] l = new int[n+1][m+1];
        int[][] r = new int[n+1][m+1];
        int[][] up = new int[n+1][m+1];
        int ans = 0;
        sc.nextLine();
        for(int i = 1;i<=n;i++){
            String[] s = sc.nextLine().split(" ");
            for(int j = 1;j<=m;j++){
                if(s[j-1].equals("F")){
                    map[i][j] = 1;
                }else{
                    map[i][j] = 2;
                }
            }
        }

        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                l[i][j] = r[i][j] = j;
                up[i][j] = 1;
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 2;j<=m;j++){
                if(map[i][j] == map[i][j-1] && map[i][j] == 1){
                    l[i][j] = l[i][j-1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = m-1;j>=1;j--){
                if(map[i][j] == map[i][j+1] && map[i][j] == 1){
                    r[i][j] = r[i][j+1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                if(i > 1){
                    if(map[i][j] == map[i-1][j] && map[i][j] == 1){
                        l[i][j] = Math.max(l[i][j],l[i-1][j]);
                        r[i][j] = Math.min(r[i][j],r[i-1][j]);
                        up[i][j] = up[i-1][j] + 1;
                    }
                }
                int a = r[i][j] - l[i][j] + 1;//极大子矩形的宽
                ans = Math.max(a*up[i][j],ans);//最大子矩形的面积
            }
        }
        System.out.println(ans*3);
    }
}

题目描述
农夫约翰想要在他的正方形农场上建造一座正方形大牛棚。他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方。我们假定,他的农场划分成 N x N 的方格。输入数据中包括有树的方格的列表。你的任务是计算并输出,在他的农场中,不需要砍树却能够修建的最大正方形牛棚。牛棚的边必须和水平轴或者垂直轴平行。

EXAMPLE

考虑下面的方格,它表示农夫约翰的农场,‘.'表示没有树的方格,‘#'表示有树的方格

1 2 3 4 5 6 7 8

1 . . . . . . . .

2 . # . . . # . .

3 . . . . . . . .

4 . . . . . . . .

5 . . . . . . . .

6 . . # . . . . .

7 . . . . . . . .

8 . . . . . . . .

最大的牛棚是 5 x 5 的,可以建造在方格右下角的两个位置其中一个。

输入格式
Line 1: 两个整数: N (1 <= N <= 1000),农场的大小,和 T (1 <= T <= 10,000)有树的方格的数量

Lines 2…T+1: 两个整数(1 <= 整数 <= N), 有树格子的横纵坐标

输出格式
只由一行组成,约翰的牛棚的最大边长。

package wttwtw;/**
 * @author by xxz
 * @date 2022/3/29
 */

import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description

 * @author by xxz

 * @date 2022/3/29

 * @throws
 */
public class xuan {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int t = sc.nextInt();
        int[][] map = new int[n+1][n+1];
        int[][] l = new int[n+1][n+1];
        int[][] r = new int[n+1][n+1];
        int[][] up = new int[n+1][n+1];
        int ans = 0;
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                map[i][j] = 1;
            }
        }
        for(int i = 0;i<t;i++){
            int a = sc.nextInt();
            int b = sc.nextInt();
            map[a][b] = 2;//2表示障碍
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                l[i][j] = r[i][j] = j;
                up[i][j] = 1;
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 2;j<=n;j++){
                if(map[i][j] == map[i][j-1] && map[i][j] == 1){
                    l[i][j] = l[i][j-1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = n-1;j>=1;j--){
                if(map[i][j] == map[i][j+1] && map[i][j] == 1){
                    r[i][j] = r[i][j+1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                if(i > 1){
                    if(map[i][j] == map[i-1][j] && map[i][j] == 1){
                        l[i][j] = Math.max(l[i][j],l[i-1][j]);
                        r[i][j] = Math.min(r[i][j],r[i-1][j]);
                        up[i][j] = up[i-1][j] + 1;
                    }
                }
                int a = r[i][j] - l[i][j] + 1;//极大子矩形的宽
                int b = Math.min(a,up[i][j]);//极大子正方形的边长
                ans = Math.max(b,ans);//最大子正方形的边长
            }
        }
        System.out.println(ans);
    }
}

小Q找到了一张由N×M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。
不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。

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

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


import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[][] map = new int[n+1][m+1];
        int[][] l = new int[n+1][m+1];
        int[][] r = new int[n+1][m+1];
        int[][] up = new int[n+1][m+1];
        int ans = 0;
        int res = 0;
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                map[i][j] = sc.nextInt();
            }
        }

        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                l[i][j] = r[i][j] = j;
                up[i][j] = 1;
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 2;j<=m;j++){
                if(map[i][j] != map[i][j-1]){
                    l[i][j] = l[i][j-1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = m-1;j>=1;j--){
                if(map[i][j] != map[i][j+1]){
                    r[i][j] = r[i][j+1];
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=m;j++){
                if(i > 1){
                    if(map[i][j] != map[i-1][j]){
                        l[i][j] = Math.max(l[i][j],l[i-1][j]);
                        r[i][j] = Math.min(r[i][j],r[i-1][j]);
                        up[i][j] = up[i-1][j] + 1;
                    }
                }
                int a = r[i][j] - l[i][j] + 1;//极大子矩形的宽
                int b = Math.min(a,up[i][j]);//极大子正方形的边长
                ans = Math.max(a*up[i][j],ans);//最大子矩形的面积
                res = Math.max(b*b,res);//最大子正方形的面积
            }
        }
        System.out.println(res);
        System.out.println(ans);

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Uranus^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值