解题思路

这是一个矩阵分割问题。要求将矩阵横竖各切三刀分成16份,使得最小的一份的价值尽可能大。可以使用二分查找来确定最优值,并用二维前缀和优化区域和的计算。

算法步骤:

  1. 预处理二维前缀和数组
  2. 二分查找可能的最小值
  3. 对每个最小值,检查是否存在合法的切割方案:
    • 枚举三个横切位置
    • 从左到右贪心地寻找四个竖切位置
    • 如果找到合法方案则返回true
  4. 返回最大的可行最小值

代码

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

const int MAXN = 76;
int matrix[MAXN][MAXN];
int preSum[MAXN][MAXN];
char line[MAXN];

// 计算区域和
int getRegionSum(int row1, int col1, int row2, int col2) {
    return preSum[row2][col2] - preSum[row2][col1] 
           - preSum[row1][col2] + preSum[row1][col1];
}

// 检查给定最小值是否可行
bool checkMinValue(int rows, int cols, int target) {
    for (int cut1 = 1; cut1 < rows - 2; ++cut1) {
        for (int cut2 = cut1 + 1; cut2 < rows - 1; ++cut2) {
            for (int cut3 = cut2 + 1; cut3 < rows; ++cut3) {
                int prevCut = 0, validCuts = 0;
                
                // 贪心寻找竖切位置
                for (int currCol = 1; currCol <= cols; ++currCol) {
                    int area1 = getRegionSum(0, prevCut, cut1, currCol);
                    int area2 = getRegionSum(cut1, prevCut, cut2, currCol);
                    int area3 = getRegionSum(cut2, prevCut, cut3, currCol);
                    int area4 = getRegionSum(cut3, prevCut, rows, currCol);
                    
                    if (min({area1, area2, area3, area4}) >= target) {
                        prevCut = currCol;
                        validCuts++;
                    }
                    if (validCuts >= 4) return true;
                }
            }
        }
    }
    return false;
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    
    // 读取矩阵
    for (int i = 0; i < n; ++i) {
        scanf("%s", line);
        for (int j = 0; j < m; ++j) {
            matrix[i][j] = line[j] - '0';
        }
    }
    
    // 计算前缀和
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] 
                          + matrix[i-1][j-1];
        }
    }
    
    // 二分查找最优解
    int left = 0, right = preSum[n][m];
    int result = 0;
    
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (checkMinValue(n, m, mid)) {
            result = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    printf("%d\n", result);
    return 0;
}
import java.util.*;

public class Main {
    private int getRegionSum(int[][] preSum, int row1, int col1, int row2, int col2) {
        return preSum[row2][col2] - preSum[row2][col1] 
               - preSum[row1][col2] + preSum[row1][col1];
    }
    
    private boolean checkMinValue(int[][] preSum, int rows, int cols, int target) {
        for (int cut1 = 1; cut1 < rows - 2; cut1++) {
            for (int cut2 = cut1 + 1; cut2 < rows - 1; cut2++) {
                for (int cut3 = cut2 + 1; cut3 < rows; cut3++) {
                    int prevCut = 0;
                    int validCuts = 0;
                    
                    // 贪心寻找竖切位置
                    for (int currCol = 1; currCol <= cols; currCol++) {
                        int area1 = getRegionSum(preSum, 0, prevCut, cut1, currCol);
                        int area2 = getRegionSum(preSum, cut1, prevCut, cut2, currCol);
                        int area3 = getRegionSum(preSum, cut2, prevCut, cut3, currCol);
                        int area4 = getRegionSum(preSum, cut3, prevCut, rows, currCol);
                        
                        if (Math.min(Math.min(area1, area2), Math.min(area3, area4)) >= target) {
                            prevCut = currCol;
                            validCuts++;
                        }
                        
                        if (validCuts >= 4) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    public int solve(char[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        
        // 计算前缀和
        int[][] preSum = new int[rows + 1][cols + 1];
        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= cols; j++) {
                preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] 
                              + (matrix[i-1][j-1] - '0');
            }
        }
        
        // 二分查找最优解
        int left = 0, right = preSum[rows][cols];
        int result = 0;
        
        while (left <= right) {
            int mid = (left + right) >>> 1;
            if (checkMinValue(preSum, rows, cols, mid)) {
                result = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        sc.nextLine();
        
        char[][] matrix = new char[n][m];
        for (int i = 0; i < n; i++) {
            matrix[i] = sc.nextLine().toCharArray();
        }
        
        Main solution = new Main();
        System.out.println(solution.solve(matrix));
        
        sc.close();
    }
}
class Solution:
    def get_region_sum(self, pre_sum, row1, col1, row2, col2):
        return pre_sum[row2][col2] - pre_sum[row2][col1] \
               - pre_sum[row1][col2] + pre_sum[row1][col1]
    
    def check_min_value(self, pre_sum, rows, cols, target):
        for cut1 in range(1, rows - 2):
            for cut2 in range(cut1 + 1, rows - 1):
                for cut3 in range(cut2 + 1, rows):
                    prev_cut = 0
                    valid_cuts = 0
                    
                    # 贪心寻找竖切位置
                    for curr_col in range(1, cols + 1):
                        area1 = self.get_region_sum(pre_sum, 0, prev_cut, cut1, curr_col)
                        area2 = self.get_region_sum(pre_sum, cut1, prev_cut, cut2, curr_col)
                        area3 = self.get_region_sum(pre_sum, cut2, prev_cut, cut3, curr_col)
                        area4 = self.get_region_sum(pre_sum, cut3, prev_cut, rows, curr_col)
                        
                        if min(area1, area2, area3, area4) >= target:
                            prev_cut = curr_col
                            valid_cuts += 1
                        
                        if valid_cuts >= 4:
                            return True
        return False
    
    def solve(self, matrix):
        rows, cols = len(matrix), len(matrix[0])
        
        # 计算前缀和
        pre_sum = [[0] * (cols + 1) for _ in range(rows + 1)]
        for i in range(1, rows + 1):
            for j in range(1, cols + 1):
                pre_sum[i][j] = pre_sum[i-1][j] + pre_sum[i][j-1] - pre_sum[i-1][j-1] \
                               + int(matrix[i-1][j-1])
        
        # 二分查找最优解
        left, right = 0, pre_sum[rows][cols]
        result = 0
        
        while left <= right:
            mid = (left + right) // 2
            if self.check_min_value(pre_sum, rows, cols, mid):
                result = mid
                left = mid + 1
            else:
                right = mid - 1
        
        return result

# 读取输入
n, m = map(int, input().split())
matrix = [input() for _ in range(n)]

solution = Solution()
print(solution.solve(matrix))

算法及复杂度

算法

  • 二分查找
  • 贪心
  • 前缀和

时间复杂度

  • 预处理前缀和:
  • 二分查找:,其中sum是矩阵元素总和
  • 检查每个最小值:
  • 总时间复杂度:

空间复杂度

  • 前缀和数组:
  • 总空间复杂度: