正整数矩阵(Py/Java/C++/Js/Go)题解
美团研发岗 0509笔试 第二题
题目内容
给定一个 n × mn \times mn × m 的正整数矩阵 AAA,其中第 iii 行第 jjj 列的元素为 Ai,jA_{i,j}Ai,j。
你可以进行若干次操作。每次操作选择两个不同的格子 (x1, y1)(x_1, y_1)(x1, y1) 与 (x2, y2)(x_2, y_2)(x2, y2),执行:
Ax1, y1 ← Ax1, y1 − 1A_{x_1, y_1} \leftarrow A_{x_1, y_1} - 1Ax1, y1 ← Ax1, y1 − 1, Ax2, y2←Ax2, y2+1\ A_{x_2, y_2} \leftarrow A_{x_2, y_2} + 1 Ax2, y2←Ax2, y2+1。
操作过程中矩阵元素必须始终为非负整数,且不允许出现负数。
你希望经过若干次操作后得到一个矩阵 BBB,使得它同时满足"上下对称"和"左右对称",即:
对所有 1 ≤ i ≤ n1 \leq i \leq n1 ≤ i ≤ n,1 ≤ j ≤ m1 \leq j \leq m1 ≤ j ≤ m,都有 Bi,j = Bn+1−i, jB_{i,j} = B_{n+1-i, j}Bi,j = Bn+1−i, j。
对所有 1 ≤ i ≤ n1 \leq i \leq n1 ≤ i ≤ n,1 ≤ j ≤ m1 \leq j \leq m1 ≤ j ≤ m,都有 Bi,j = Bi, m+1−jB_{i,j} = B_{i, m+1-j}Bi,j = Bi, m+1−j。
请你输出任意一个满足条件的矩阵 BBB;如果不存在,输出 −1-1−1。
输入描述
每个测试文件包含多组测试数据。第一行输入一个整数 T(1 ≤ T ≤ 105)T(1 \leq T \leq 10^5)T(1 ≤ T ≤ 105) 代表数据组数,每组测试数据描述如下:
第一行输入两个整数 n, m(1 ≤ n, m ≤ 2 × 105)n, m(1 \leq n, m \leq 2 \times 10^5)n, m(1 ≤ n, m ≤ 2 × 105)。
接下来输入 nnn 行,每行输入 mmm 个整数,第 iii 行第 jjj 列为 Aij (0≤Aij≤109)A_{ij} \ (0 \leq A_{ij} \leq 10^9)Aij (0≤Aij≤109),表示矩阵 AAA。
保证单个测试文件中所有测试数据的 n ⋅ mn \cdot mn ⋅ m 之和不超过 5 × 1055 \times 10^55 × 105。
输出描述
对于每组测试数据:
如果不存在满足条件的矩阵,输出一行 −1-1−1。
否则输出 nnn 行,每行输出 mmm 个非负整数,表示一个合法的矩阵 BBB。
样例1
输入
3
2 2
1 2
3 4
1 3
1 5 10
2 2
1 1
1 2
输出
-1
5 6 5
-1
题解
思路
逻辑分析
- 题目只允许
任选两个格子 A[x1][y1]-- A[x2][y2]++操作,本质上是在矩阵内搬运单位价值,其实本质上只限制了最终生成矩阵元素总和不变。所以问题转变为是否存在一个同时上下对称、左右对称的非负整数矩阵 B,使得元素和等于 S - 接下来进行分析
n m情况进行讨论:n 和 m都为偶数情况下: 矩阵会被划分为4个区域,四个区域彼此不会重叠,可以假设每个区域总和为a, 四个区域的总和为4a, 所以要满足S可以划分为四个区域,必须满足s % 4 == 0n 和 m 区域 存在一奇一偶情况:相比n m 都为偶情况下,增加了中间行,中间行只会影响左右/上下一个方向,可以先考虑不考虑中间行情况,将矩阵划分为四个区域(总和为a),再额外考虑中间行,只会影响一个方向,设置中间行一半为b,所以矩阵总和为4a + 2b,要想满足这个条件S % 2 == 0n和m区域都为奇数情况:相比n m 一奇 一偶情况,增加中心对称点,可以假设对称点和为c,类似其实可以将和转换为4a + 2b + c, c可以为奇数和偶数,总会存在这样的矩阵。
- 按照2的规律可以快速判断出是否存在合法矩阵。可以容易得出 3 处理中心点之后就变为2,2中间边处理之后就变为1情况,为了使得构造简单我们只设置每个区域的边界点,可以只对以下位置设值,其它可以不考虑就行,对应处理
- 中间点,
设置中心值,使得S总和变为偶数 - 中间边两个端点,
设置两个端点,S变为4的倍数 - 对应四个区域的左上点、右上角、左下角、右下角。设置每个值为
S/4
- 中间点,
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
long sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
long x;
cin >> x;
sum += x;
}
}
// 判断是否有解
bool ok = true;
if (n % 2 == 0 && m % 2 == 0) {
ok = (sum % 4) == 0;
// 一奇一偶
}else if ((n % 2) != (m % 2)) {
ok = (sum % 2 == 0);
}
if (!ok) {
cout << "-1" << endl;
continue;
}
// 构造结果
vector<vector<long>> b(n, vector<long>(m, 0));
long rem = sum;
if ((n & 1) && (m & 1) && (rem & 1)) {
b[n / 2][m / 2] = 1;
rem--;
}
// 2元组(贡献2)
if (rem % 4 == 2) {
if (n & 1) {
int r = n / 2;
b[r][0] += 1;
b[r][m-1] += 1;
rem -= 2;
} else {
int c = m / 2;
b[0][c] += 1;
b[n-1][c] += 1;
rem -= 2;
}
}
// 剩下一定是4的倍数
long v = rem / 4;
b[0][0] += v;
b[0][m-1] += v;
b[n-1][0] += v;
b[n-1][m-1] += v;
// 输出结果
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (j > 0) {
cout << " ";
}
cout << b[i][j];
}
cout << endl;
}
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder out = new StringBuilder();
int T = Integer.parseInt(br.readLine().trim());
while (T-- > 0) {
String[] parts = br.readLine().trim().split(" ");
int n = Integer.parseInt(parts[0]);
int m = Integer.parseInt(parts[1]);
long sum = 0;
for (int i = 0; i < n; i++) {
String[] row = br.readLine().trim().split(" ");
for (int j = 0; j < m; j++) {
long x = Long.parseLong(row[j]);
sum += x;
}
}
// 判断是否有解
boolean ok = true;
if (n % 2 == 0 && m % 2 == 0) {
ok = (sum % 4) == 0;
} else if ((n % 2) != (m % 2)) {
ok = (sum % 2 == 0);
}
if (!ok) {
out.append("-1\n");
continue;
}
// 构造结果
long[][] b = new long[n][m];
long rem = sum;
if ((n % 2 == 1) && (m % 2 == 1) && (rem % 2 == 1)) {
b[n / 2][m / 2] = 1;
rem--;
}
// 2元组(贡献2)
if (rem % 4 == 2) {
if ((n & 1) == 1) {
int r = n / 2;
b[r][0] += 1;
b[r][m - 1] += 1;
rem -= 2;
} else {
int c = m / 2;
b[0][c] += 1;
b[n - 1][c] += 1;
rem -= 2;
}
}
// 剩下一定是4的倍数
long v = rem / 4;
b[0][0] += v;
b[0][m - 1] += v;
b[n - 1][0] += v;
b[n - 1][m - 1] += v;
// 输出结果
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (j > 0) out.append(" ");
out.append(b[i][j]);
}
out.append("\n");
}
}
System.out.print(out.toString());
}
}
python
python
import sys
input = sys.stdin.readline
T = int(input())
for _ in range(T):
n, m = map(int, input().split())
total = 0
for _ in range(n):
row = list(map(int, input().split()))
total += sum(row)
# 判断是否有解
ok = True
if n % 2 == 0 and m % 2 == 0:
ok = (total % 4 == 0)
elif (n % 2) != (m % 2):
ok = (total % 2 == 0)
if not ok:
print(-1)
continue
# 构造结果
b = [[0] * m for _ in range(n)]
rem = total
if (n % 2 == 1) and (m % 2 == 1) and (rem % 2 == 1):
b[n // 2][m // 2] = 1
rem -= 1
# 2元组(贡献2)
if rem % 4 == 2:
if n % 2 == 1:
r = n // 2
b[r][0] += 1
b[r][m - 1] += 1
rem -= 2
else:
c = m // 2
b[0][c] += 1
b[n - 1][c] += 1
rem -= 2
# 剩余4的倍数
v = rem // 4
b[0][0] += v
b[0][m - 1] += v
b[n - 1][0] += v
b[n - 1][m - 1] += v
for i in range(n):
print(" ".join(map(str, b[i])))
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on("line", (line) => {
input.push(line.trim());
});
rl.on("close", () => {
let idx = 0;
let T = Number(input[idx++]);
while (T--) {
let [n, m] = input[idx++].split(" ").map(Number);
let sum = 0;
for (let i = 0; i < n; i++) {
let row = input[idx++].split(" ").map(Number);
for (let j = 0; j < m; j++) {
sum += row[j];
}
}
// 判断是否有解
let ok = true;
if (n % 2 === 0 && m % 2 === 0) {
ok = (sum % 4 === 0);
} else if ((n % 2) !== (m % 2)) {
ok = (sum % 2 === 0);
}
if (!ok) {
console.log("-1");
continue;
}
// 构造结果
let b = Array.from({ length: n }, () => Array(m).fill(0));
let rem = sum;
if ((n % 2 === 1) && (m % 2 === 1) && (rem % 2 === 1)) {
b[Math.floor(n / 2)][Math.floor(m / 2)] = 1;
rem--;
}
// 2元组(贡献2)
if (rem % 4 === 2) {
if (n % 2 === 1) {
let r = Math.floor(n / 2);
b[r][0] += 1;
b[r][m - 1] += 1;
rem -= 2;
} else {
let c = Math.floor(m / 2);
b[0][c] += 1;
b[n - 1][c] += 1;
rem -= 2;
}
}
// 剩余4的倍数
let v = Math.floor(rem / 4);
b[0][0] += v;
b[0][m - 1] += v;
b[n - 1][0] += v;
b[n - 1][m - 1] += v;
for (let i = 0; i < n; i++) {
console.log(b[i].join(" "));
}
}
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var T int
fmt.Fscan(in, &T)
for T > 0 {
T--
var n, m int
fmt.Fscan(in, &n, &m)
sum := int64(0)
a := make([][]int64, n)
for i := 0; i < n; i++ {
a[i] = make([]int64, m)
for j := 0; j < m; j++ {
fmt.Fscan(in, &a[i][j])
sum += a[i][j]
}
}
// 判断是否有解
ok := true
if n%2 == 0 && m%2 == 0 {
ok = (sum%4 == 0)
} else if (n%2) != (m%2) {
ok = (sum%2 == 0)
}
if !ok {
fmt.Fprintln(out, -1)
continue
}
// 构造结果
b := make([][]int64, n)
for i := range b {
b[i] = make([]int64, m)
}
rem := sum
if (n%2 == 1) && (m%2 == 1) && (rem%2 == 1) {
b[n/2][m/2] = 1
rem--
}
// 2元组(贡献2)
if rem%4 == 2 {
if n%2 == 1 {
r := n / 2
b[r][0]++
b[r][m-1]++
rem -= 2
} else {
c := m / 2
b[0][c]++
b[n-1][c]++
rem -= 2
}
}
// 剩余4的倍数
v := rem / 4
b[0][0] += v
b[0][m-1] += v
b[n-1][0] += v
b[n-1][m-1] += v
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if j > 0 {
fmt.Fprint(out, " ")
}
fmt.Fprint(out, b[i][j])
}
fmt.Fprintln(out)
}
}
}