坚果巧克力(Java/Py/C/C++/Js/Go)题解
华为OD机试真题 新系统 华为OD上机考试真题新系统 9月27号 200分题型
华为OD机试新系统真题目录点击查看: 华为OD机试新系统真题题库目录|机考题库 + 算法考点详解
题目内容
有一块 n × m n \times m n×m( 4 ≤ n , m ≤ 50 4 \leq n, m \leq 50 4≤n,m≤50)的矩形巧克力,每个格子有一个正整数权值(权值 ≤ 1000 \leq 1000 ≤1000),代表这一格的坚果数量。
必须沿着格子横切恰好 3 3 3 刀、竖切恰好 3 3 3 刀,将巧克力分成 4 × 4 = 16 4 \times 4 = 16 4×4=16 块,分给 16 16 16 个人。
切分难以保证每个人分到的一样多,因此你想照顾那个分得最少的人 ,请通过合理安排切分方式,使这个"分得最少的人"分到的坚果数量在所有切分方式中最多(即:求一种划分方式,最大化这 16 16 16 块中权值和最小的那一块的权值和,请输出这个最大化的最小权值和。)
输入描述
输入包含一行,格式为 n,m,[[a11,a12,...,a1m],[a21,...,a2m],...,[an1,...,anm]],其中 n n n 为行数, m m m 为列数,后续为 n n n 行 m m m 列的矩阵,表示每个格子的权值。
输出描述
输出一个整数,表示在最优切分方案下, 16 16 16 块巧克力中权值和最小的那一块所能达到的最大权值和。
样例1
输入
4 7
2 1 1 1 2 1 1
2 2 2 2 2 2 2
1 2 3 4 5 6 7
7 6 5 4 3 2 1
输出
2
说明
- 只有四行,只能每行都分开
- 第一行最紧张, 7 7 7 格子分 4 4 4 块,一定会有一块只能分到 1 1 1 格,选择第一列的 2 2 2 分得 1 1 1 格,会导致第三行第一列的和只有 1 1 1,因此想要获得超过 1 1 1 的结果只能选择第 5 5 5 列前后进行划分
- 在这个基础上,一种可行的划分方式是:
- 第 1 1 1 行:
2,1|1,1|2|1,1 - 第 2 2 2 行:
2,2|2,2|2|2,2 - 第 3 3 3 行:
1,2|3,4|5|6,7 - 第 4 4 4 行:
7,6|5,4|3|2,1 - 此时第 1 1 1 行的
|1,1|、|2|、|1,1|三块为最小,但是都有 2 2 2 的权值和
样例2
输入
5 6
1 1 1 5 5 5
1 1 1 1 1 1
6 6 6 6 6 6
6 6 6 6 6 6
6 6 6 6 6 6
输出
6
说明
- 一种可行的划分是:
- 第 1 1 1 行:
1,1,1|5|5|5 - 第 2 2 2 行:
1,1,1|1|1|1 - 第 3 3 3 行:
6,6,6|6|6|6 - 第 4 4 4 行:
6,6,6|6|6|6 - 第 5 5 5 行:
6,6,6|6|6|6 - 这种划分保障了在正确划分的基础上左上角至少获得了 6 6 6 的权值
解题思路
思路:二分 + 贪心 + 前缀和
- 题目要求
最大化这16块中权值和最小的那一块的权值和, 如果x可以,那么答案至少为x,如果不可以答案一定小于x,所以具备单调性,可以进行二分。 - 通过二分枚举确定每个块最小权值和,下界
left = 0, 上界设置right = sum(grid) / 16, 每次枚举x = (left + right + 1)/ 2, 然后判断是否能满足16块每块和都不小于x- 满足分配的情况下更新
left = x - 不满足分配更新
right = x - 1
- 满足分配的情况下更新
- 检验是否满足枚举x,采用枚举 + 贪心
- 通过枚举确定行切的三刀位置,确定行分块布局,
- 然后贪心分配列,当前检查四块的和都大于等于x时,就可以确定列切一到的位置。最终判断是否可以满足列切三刀,对应16块是否都大于等于x
- 不同行切位置枚举只有存在一种合法情况,就返回true,否则返回false。
C++
cpp
#include <bits/stdc++.h>
using namespace std;
int solve(int n, int m, vector<vector<int>>& grid) {
// 让枚举三刀的方向尽可能小
vector<vector<int>> g = grid;
if (n > m) {
vector<vector<int>> t(m, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
t[j][i] = g[i][j];
}
}
g.swap(t);
swap(n, m);
}
// 此时 n <= m
// pref[i][j]:第 j 列前 i 行的和
vector<vector<long long>> pref(n + 1, vector<long long>(m, 0));
long long total = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
pref[i + 1][j] = pref[i][j] + g[i][j];
total += g[i][j];
}
}
auto can = [&](long long mid) -> bool {
if (mid <= 0) return true;
// 16 个区域都至少为 mid,则总和至少为 16 * mid
if (total < 16LL * mid) return false;
// 枚举 3 条横刀
for (int a = 1; a <= n - 3; a++) {
for (int b = a + 1; b <= n - 2; b++) {
for (int c = b + 1; c <= n - 1; c++) {
int j = 0;
bool ok = true;
// 贪心寻找前 3 个竖向区域
for (int gg = 0; gg < 3; gg++) {
if (j >= m) {
ok = false;
break;
}
long long s0 = 0;
long long s1 = 0;
long long s2 = 0;
long long s3 = 0;
// 当前竖向区域只要有一个横向块没有达到 mid
// 就继续增加列
while (j < m &&
(s0 < mid || s1 < mid ||
s2 < mid || s3 < mid)) {
// 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j];
// 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j];
// 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j];
// 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j];
j++;
}
// 当前竖向区域无法让 4 个块全部达到 mid
if (s0 < mid || s1 < mid ||
s2 < mid || s3 < mid) {
ok = false;
break;
}
}
// 前 3 个竖向区域已经完成
// 剩余所有列组成第 4 个竖向区域
if (!ok || j >= m) continue;
long long s0 = 0;
long long s1 = 0;
long long s2 = 0;
long long s3 = 0;
while (j < m) {
s0 += pref[a][j] - pref[0][j];
s1 += pref[b][j] - pref[a][j];
s2 += pref[c][j] - pref[b][j];
s3 += pref[n][j] - pref[c][j];
j++;
}
// 4 个横向区域在最后一个竖向区域中
// 都达到 mid
if (s0 >= mid && s1 >= mid &&
s2 >= mid && s3 >= mid) {
return true;
}
}
}
}
return false;
};
// 最小块的值不可能超过平均值
long long lo = 0;
long long hi = total / 16;
long long ans = 0;
while (lo <= hi) {
long long mid = lo + (hi - lo) / 2;
if (can(mid)) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return (int)ans;
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
cout << solve(n, m, grid);
return 0;
}
JAVA
java
import java.io.*;
import java.util.*;
public class Main {
static int solve(int n, int m, int[][] grid) {
// 让枚举三刀的方向尽可能小
int[][] g = grid;
if (n > m) {
int[][] t = new int[m][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
t[j][i] = g[i][j];
}
}
g = t;
int tmp = n;
n = m;
m = tmp;
}
// 此时 n <= m
// pref[i][j]:第 j 列前 i 行的和
long[][] pref = new long[n + 1][m];
long total = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
pref[i + 1][j] = pref[i][j] + g[i][j];
total += g[i][j];
}
}
long lo = 0;
long hi = total / 16;
long ans = 0;
while (lo <= hi) {
long mid = lo + (hi - lo) / 2;
boolean ok = false;
if (mid <= 0) {
ok = true;
} else if (total >= 16L * mid) {
// 枚举 3 条横刀
for (int a = 1; a <= n - 3 && !ok; a++) {
for (int b = a + 1; b <= n - 2 && !ok; b++) {
for (int c = b + 1; c <= n - 1 && !ok; c++) {
int j = 0;
boolean valid = true;
// 贪心寻找前 3 个竖向区域
for (int gg = 0; gg < 3; gg++) {
if (j >= m) {
valid = false;
break;
}
long s0 = 0;
long s1 = 0;
long s2 = 0;
long s3 = 0;
// 当前竖向区域只要有一个横向块没有达到 mid
// 就继续增加列
while (j < m &&
(s0 < mid || s1 < mid ||
s2 < mid || s3 < mid)) {
// 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j];
// 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j];
// 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j];
// 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j];
j++;
}
// 当前竖向区域无法让 4 个块全部达到 mid
if (s0 < mid || s1 < mid ||
s2 < mid || s3 < mid) {
valid = false;
break;
}
}
// 前 3 个竖向区域已经完成
// 剩余所有列组成第 4 个竖向区域
if (!valid || j >= m) {
continue;
}
long s0 = 0;
long s1 = 0;
long s2 = 0;
long s3 = 0;
while (j < m) {
s0 += pref[a][j] - pref[0][j];
s1 += pref[b][j] - pref[a][j];
s2 += pref[c][j] - pref[b][j];
s3 += pref[n][j] - pref[c][j];
j++;
}
// 4 个横向区域在最后一个竖向区域中
// 都达到 mid
if (s0 >= mid && s1 >= mid &&
s2 >= mid && s3 >= mid) {
ok = true;
}
}
}
}
}
if (ok) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return (int) ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] grid = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
grid[i][j] = sc.nextInt();
}
}
System.out.print(solve(n, m, grid));
}
}
Python
python
import sys
def solve(n, m, grid):
# 让枚举三刀的方向尽可能小
g = grid
if n > m:
t = [[0] * n for _ in range(m)]
for i in range(n):
for j in range(m):
t[j][i] = g[i][j]
g = t
n, m = m, n
# 此时 n <= m
# pref[i][j]:第 j 列前 i 行的和
pref = [[0] * m for _ in range(n + 1)]
total = 0
for i in range(n):
for j in range(m):
pref[i + 1][j] = pref[i][j] + g[i][j]
total += g[i][j]
def can(mid):
if mid <= 0:
return True
# 16 个区域都至少为 mid,则总和至少为 16 * mid
if total < 16 * mid:
return False
# 枚举 3 条横刀
for a in range(1, n - 2):
for b in range(a + 1, n - 1):
for c in range(b + 1, n):
j = 0
ok = True
# 贪心寻找前 3 个竖向区域
for gg in range(3):
if j >= m:
ok = False
break
s0 = 0
s1 = 0
s2 = 0
s3 = 0
# 当前竖向区域只要有一个横向块没有达到 mid
# 就继续增加列
while j < m and (s0 < mid or s1 < mid or
s2 < mid or s3 < mid):
# 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j]
# 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j]
# 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j]
# 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j]
j += 1
# 当前竖向区域无法让 4 个块全部达到 mid
if s0 < mid or s1 < mid or s2 < mid or s3 < mid:
ok = False
break
# 前 3 个竖向区域已经完成
# 剩余所有列组成第 4 个竖向区域
if not ok or j >= m:
continue
s0 = 0
s1 = 0
s2 = 0
s3 = 0
while j < m:
s0 += pref[a][j] - pref[0][j]
s1 += pref[b][j] - pref[a][j]
s2 += pref[c][j] - pref[b][j]
s3 += pref[n][j] - pref[c][j]
j += 1
# 4 个横向区域在最后一个竖向区域中
# 都达到 mid
if s0 >= mid and s1 >= mid and s2 >= mid and s3 >= mid:
return True
return False
# 最小块的值不可能超过平均值
lo = 0
hi = total // 16
ans = 0
while lo <= hi:
mid = lo + (hi - lo) // 2
if can(mid):
ans = mid
lo = mid + 1
else:
hi = mid - 1
return int(ans)
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
idx = 0
n = data[idx]
m = data[idx + 1]
idx += 2
grid = []
for _ in range(n):
grid.append(data[idx:idx + m])
idx += m
print(solve(n, m, grid))
if __name__ == "__main__":
main()
JavaScript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on('line', line => {
input.push(...line.trim().split(/\s+/).filter(Boolean).map(Number));
});
rl.on('close', () => {
let idx = 0;
const n0 = input[idx++];
const m0 = input[idx++];
const grid = Array.from({ length: n0 }, () => {
const row = [];
for (let j = 0; j < m0; j++) {
row.push(input[idx++]);
}
return row;
});
console.log(solve(n0, m0, grid));
});
function solve(n, m, grid) {
// 让枚举三刀的方向尽可能小
let g = grid;
if (n > m) {
const t = Array.from({ length: m }, () => Array(n).fill(0));
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
t[j][i] = g[i][j];
}
}
g = t;
[n, m] = [m, n];
}
// 此时 n <= m
// pref[i][j]:第 j 列前 i 行的和
const pref = Array.from({ length: n + 1 }, () => Array(m).fill(0));
let total = 0;
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
pref[i + 1][j] = pref[i][j] + g[i][j];
total += g[i][j];
}
}
function can(mid) {
if (mid <= 0) return true;
// 16 个区域都至少为 mid,则总和至少为 16 * mid
if (total < 16 * mid) return false;
// 枚举 3 条横刀
for (let a = 1; a <= n - 3; a++) {
for (let b = a + 1; b <= n - 2; b++) {
for (let c = b + 1; c <= n - 1; c++) {
let j = 0;
let ok = true;
// 贪心寻找前 3 个竖向区域
for (let gg = 0; gg < 3; gg++) {
if (j >= m) {
ok = false;
break;
}
let s0 = 0;
let s1 = 0;
let s2 = 0;
let s3 = 0;
// 当前竖向区域只要有一个横向块没有达到 mid
// 就继续增加列
while (j < m &&
(s0 < mid || s1 < mid ||
s2 < mid || s3 < mid)) {
// 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j];
// 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j];
// 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j];
// 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j];
j++;
}
// 当前竖向区域无法让 4 个块全部达到 mid
if (s0 < mid || s1 < mid ||
s2 < mid || s3 < mid) {
ok = false;
break;
}
}
// 前 3 个竖向区域已经完成
// 剩余所有列组成第 4 个竖向区域
if (!ok || j >= m) continue;
let s0 = 0;
let s1 = 0;
let s2 = 0;
let s3 = 0;
while (j < m) {
s0 += pref[a][j] - pref[0][j];
s1 += pref[b][j] - pref[a][j];
s2 += pref[c][j] - pref[b][j];
s3 += pref[n][j] - pref[c][j];
j++;
}
// 4 个横向区域在最后一个竖向区域中
// 都达到 mid
if (s0 >= mid && s1 >= mid &&
s2 >= mid && s3 >= mid) {
return true;
}
}
}
}
return false;
}
// 最小块的值不可能超过平均值
let lo = 0;
let hi = Math.floor(total / 16);
let ans = 0;
while (lo <= hi) {
const mid = lo + Math.floor((hi - lo) / 2);
if (can(mid)) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func solve(n int, m int, grid [][]int) int {
// 让枚举三刀的方向尽可能小
g := grid
if n > m {
t := make([][]int, m)
for i := 0; i < m; i++ {
t[i] = make([]int, n)
}
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
t[j][i] = g[i][j]
}
}
g = t
n, m = m, n
}
// 此时 n <= m
// pref[i][j]:第 j 列前 i 行的和
pref := make([][]int64, n+1)
for i := 0; i <= n; i++ {
pref[i] = make([]int64, m)
}
var total int64
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
pref[i+1][j] = pref[i][j] + int64(g[i][j])
total += int64(g[i][j])
}
}
can := func(mid int64) bool {
if mid <= 0 {
return true
}
// 16 个区域都至少为 mid,则总和至少为 16 * mid
if total < 16*mid {
return false
}
// 枚举 3 条横刀
for a := 1; a <= n-3; a++ {
for b := a + 1; b <= n-2; b++ {
for c := b + 1; c <= n-1; c++ {
j := 0
ok := true
// 贪心寻找前 3 个竖向区域
for gg := 0; gg < 3; gg++ {
if j >= m {
ok = false
break
}
var s0, s1, s2, s3 int64
// 当前竖向区域只要有一个横向块没有达到 mid
// 就继续增加列
for j < m && (s0 < mid || s1 < mid ||
s2 < mid || s3 < mid) {
// 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j]
// 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j]
// 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j]
// 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j]
j++
}
// 当前竖向区域无法让 4 个块全部达到 mid
if s0 < mid || s1 < mid ||
s2 < mid || s3 < mid {
ok = false
break
}
}
// 前 3 个竖向区域已经完成
// 剩余所有列组成第 4 个竖向区域
if !ok || j >= m {
continue
}
var s0, s1, s2, s3 int64
while := true
for j < m && while {
s0 += pref[a][j] - pref[0][j]
s1 += pref[b][j] - pref[a][j]
s2 += pref[c][j] - pref[b][j]
s3 += pref[n][j] - pref[c][j]
j++
}
// 4 个横向区域在最后一个竖向区域中
// 都达到 mid
if s0 >= mid && s1 >= mid &&
s2 >= mid && s3 >= mid {
return true
}
}
}
}
return false
}
// 最小块的值不可能超过平均值
var lo int64 = 0
hi := total / 16
var ans int64 = 0
for lo <= hi {
mid := lo + (hi-lo)/2
if can(mid) {
ans = mid
lo = mid + 1
} else {
hi = mid - 1
}
}
return int(ans)
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var n, m int
fmt.Fscan(in, &n, &m)
grid := make([][]int, n)
for i := 0; i < n; i++ {
grid[i] = make([]int, m)
for j := 0; j < m; j++ {
fmt.Fscan(in, &grid[i][j])
}
}
fmt.Fprint(out, solve(n, m, grid))
}
C语言
cpp
#include <stdio.h>
#include <stdlib.h>
long long min_ll(long long a, long long b) {
return a < b ? a : b;
}
int solve(int n, int m, int grid[n][m]) {
// 让枚举三刀的方向尽可能小
int **g = (int **)malloc(n * sizeof(int *));
for (int i = 0; i < n; i++) {
g[i] = (int *)malloc(m * sizeof(int));
for (int j = 0; j < m; j++) {
g[i][j] = grid[i][j];
}
}
if (n > m) {
int **t = (int **)malloc(m * sizeof(int *));
for (int i = 0; i < m; i++) {
t[i] = (int *)malloc(n * sizeof(int));
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
t[j][i] = g[i][j];
}
}
for (int i = 0; i < n; i++) {
free(g[i]);
}
free(g);
g = t;
int tmp = n;
n = m;
m = tmp;
}
// 此时 n <= m
// pref[i][j]:第 j 列前 i 行的和
long long **pref = (long long **)malloc((n + 1) * sizeof(long long *));
for (int i = 0; i <= n; i++) {
pref[i] = (long long *)calloc(m, sizeof(long long));
}
long long total = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
pref[i + 1][j] = pref[i][j] + g[i][j];
total += g[i][j];
}
}
// 判断 mid 是否可行
int can(long long mid) {
if (mid <= 0) {
return 1;
}
// 16 个区域都至少为 mid,则总和至少为 16 * mid
if (total < 16LL * mid) {
return 0;
}
// 枚举 3 条横刀
for (int a = 1; a <= n - 3; a++) {
for (int b = a + 1; b <= n - 2; b++) {
for (int c = b + 1; c <= n - 1; c++) {
int j = 0;
int ok = 1;
// 贪心寻找前 3 个竖向区域
for (int gg = 0; gg < 3; gg++) {
if (j >= m) {
ok = 0;
break;
}
long long s0 = 0;
long long s1 = 0;
long long s2 = 0;
long long s3 = 0;
// 当前竖向区域只要有一个横向块没有达到 mid
// 就继续增加列
while (j < m &&
(s0 < mid || s1 < mid ||
s2 < mid || s3 < mid)) {
// 第 1 个横向区域:0 ~ a-1
s0 += pref[a][j] - pref[0][j];
// 第 2 个横向区域:a ~ b-1
s1 += pref[b][j] - pref[a][j];
// 第 3 个横向区域:b ~ c-1
s2 += pref[c][j] - pref[b][j];
// 第 4 个横向区域:c ~ n-1
s3 += pref[n][j] - pref[c][j];
j++;
}
// 当前竖向区域无法让 4 个块全部达到 mid
if (s0 < mid || s1 < mid ||
s2 < mid || s3 < mid) {
ok = 0;
break;
}
}
// 前 3 个竖向区域已经完成
// 剩余所有列组成第 4 个竖向区域
if (!ok || j >= m) {
continue;
}
long long s0 = 0;
long long s1 = 0;
long long s2 = 0;
long long s3 = 0;
while (j < m) {
s0 += pref[a][j] - pref[0][j];
s1 += pref[b][j] - pref[a][j];
s2 += pref[c][j] - pref[b][j];
s3 += pref[n][j] - pref[c][j];
j++;
}
// 4 个横向区域在最后一个竖向区域中
// 都达到 mid
if (s0 >= mid && s1 >= mid &&
s2 >= mid && s3 >= mid) {
return 1;
}
}
}
}
return 0;
}
// 最小块的值不可能超过平均值
long long lo = 0;
long long hi = total / 16;
long long ans = 0;
while (lo <= hi) {
long long mid = lo + (hi - lo) / 2;
if (can(mid)) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
for (int i = 0; i < n; i++) {
free(g[i]);
}
free(g);
for (int i = 0; i <= n; i++) {
free(pref[i]);
}
free(pref);
return (int)ans;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
int (*grid)[m] = malloc(n * sizeof(*grid));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &grid[i][j]);
}
}
printf("%d", solve(n, m, grid));
free(grid);
return 0;
}