Hard
题目描述
给你一个 m x n 的二维数组 board 代表棋盘,其中 board[i][j] 表示格子 (i, j) 的价值。
同一行或同一列的车会相互攻击。你需要在棋盘上放置三个车,使得车不会相互攻击。
返回放置车的格子的价值之和的最大值。
示例 1:
输入:board = [[-3,1,1,1],[-3,1,-3,1],[-3,2,1,1]]
输出:4
解释:我们可以在格子 (0, 2)、(1, 3) 和 (2, 1) 放置车,价值和为 1 + 1 + 2 = 4。
示例 2:
输入:board = [[1,2,3],[4,5,6],[7,8,9]]
输出:15
解释:我们可以在格子 (0, 0)、(1, 1) 和 (2, 2) 放置车,价值和为 1 + 5 + 9 = 15。
示例 3:
输入:board = [[1,1,1],[1,1,1],[1,1,1]]
输出:3
解释:我们可以在格子 (0, 2)、(1, 1) 和 (2, 0) 放置车,价值和为 1 + 1 + 1 = 3。
约束:
3 <= m == board.length <= 5003 <= n == board[i].length <= 500-10^9 <= board[i][j] <= 10^9
解题思路
这道题要求在棋盘上放置三个车,使得它们不会相互攻击(不在同一行或同一列),并且使价值和最大。
核心思路: 由于三个车必须分别在不同的行和不同的列,我们可以:
预处理优化:对于每一行,保存前3大的值及其列索引。这样可以大大减少搜索空间。
枚举策略:
- 枚举第一个车所在的行i和对应的最优列
- 对于剩余的行,收集所有不与第一个车同列的前4大值
- 从这些候选值中暴力枚举选择2个位置作为第二、三个车的位置
实现细节:
- 为了避免列冲突,我们需要在收集候选值时排除已选择的列
- 由于每行最多保存3个值,剩余所有行的候选值总数有限,可以安全地进行暴力枚举
这种方法的优势是通过预处理减少了大量不必要的计算,将时间复杂度控制在可接受范围内。
算法流程:
- 对每行计算并存储前3大的值和对应列索引
- 枚举第一个车的位置
- 收集其他行中与第一个车不同列的前4大候选值
- 暴力枚举第二、三个车的位置组合
- 更新全局最大值
代码实现
class Solution {
public:
long long maximumValueSum(vector<vector<int>>& board) {
int m = board.size(), n = board[0].size();
// 存储每行前3大的值和对应列索引
vector<vector<pair<int, int>>> rowTop3(m);
for (int i = 0; i < m; i++) {
vector<pair<int, int>> candidates;
for (int j = 0; j < n; j++) {
candidates.push_back({board[i][j], j});
}
sort(candidates.rbegin(), candidates.rend());
for (int k = 0; k < min(3, n); k++) {
rowTop3[i].push_back(candidates[k]);
}
}
long long maxSum = LLONG_MIN;
// 枚举第一个车的位置
for (int row1 = 0; row1 < m; row1++) {
for (auto& [val1, col1] : rowTop3[row1]) {
// 收集其他行的候选值(不与col1冲突)
vector<tuple<int, int, int>> candidates; // {value, row, col}
for (int i = 0; i < m; i++) {
if (i == row1) continue;
int count = 0;
for (auto& [val, col] : rowTop3[i]) {
if (col != col1 && count < 4) {
candidates.push_back({val, i, col});
count++;
}
}
}
// 按价值排序
sort(candidates.rbegin(), candidates.rend());
// 枚举第二、三个车的位置
for (int i = 0; i < candidates.size(); i++) {
for (int j = i + 1; j < candidates.size(); j++) {
auto [val2, row2, col2] = candidates[i];
auto [val3, row3, col3] = candidates[j];
if (row2 != row3 && col2 != col3) {
maxSum = max(maxSum, (long long)val1 + val2 + val3);
}
}
}
}
}
return maxSum;
}
};
class Solution:
def maximumValueSum(self, board: List[List[int]]) -> int:
m, n = len(board), len(board[0])
# 存储每行前3大的值和对应列索引
row_top3 = []
for i in range(m):
candidates = [(board[i][j], j) for j in range(n)]
candidates.sort(reverse=True)
row_top3.append(candidates[:3])
max_sum = float('-inf')
# 枚举第一个车的位置
for row1 in range(m):
for val1, col1 in row_top3[row1]:
# 收集其他行的候选值(不与col1冲突)
candidates = []
for i in range(m):
if i == row1:
continue
count = 0
for val, col in row_top3[i]:
if col != col1 and count < 4:
candidates.append((val, i, col))
count += 1
# 按价值排序
candidates.sort(reverse=True)
# 枚举第二、三个车的位置
for i in range(len(candidates)):
for j in range(i + 1, len(candidates)):
val2, row2, col2 = candidates[i]
val3, row3, col3 = candidates[j]
if row2 != row3 and col2 != col3:
max_sum = max(max_sum, val1 + val2 + val3)
return max_sum
public class Solution {
public long MaximumValueSum(int[][] board) {
int m = board.Length, n = board[0].Length;
// 存储每行前3大的值和对应列索引
var rowTop3 = new List<(int val, int col)>[m];
for (int i = 0; i < m; i++) {
var candidates = new List<(int val, int col)>();
for (int j = 0; j < n; j++) {
candidates.Add((board[i][j], j));
}
candidates.Sort((a, b) => b.val.CompareTo(a.val));
rowTop3[i] = new List<(int val, int col)>();
for (int k = 0; k < Math.Min(3, n); k++) {
rowTop3[i].Add(candidates[k]);
}
}
long maxSum = long.MinValue;
// 枚举第一个车的位置
for (int row1 = 0; row1 < m; row1++) {
foreach (var (val1, col1) in rowTop3[row1]) {
// 收集其他行的候选值(不与col1冲突)
var candidates = new List<(int val, int row, int col)>();
for (int i = 0; i < m; i++) {
if (i == row1) continue;
int count = 0;
foreach (var (val, col) in rowTop3[i]) {
if (col != col1 && count < 4) {
candidates.Add((val, i, col));
count++;
}
}
}
// 按价值排序
candidates.Sort((a, b) => b.val.CompareTo(a.val));
// 枚举第二、三个车的位置
for (int i = 0; i < candidates.Count; i++) {
for (int j = i + 1; j < candidates.Count; j++) {
var (val2, row2, col2) = candidates[i];
var (val3, row3, col3) = candidates[j];
if (row2 != row3 && col2 != col3) {
maxSum = Math.Max(maxSum, (long)val1 + val2 + val3);
}
}
}
}
}
return maxSum;
}
}
var maximumValueSum = function(board) {
const m = board.length;
const n = board[0].length;
// Get top 3 values for each row
const rowTop3 = [];
for (let i = 0; i < m; i++) {
const row = [];
for (let j = 0; j < n; j++) {
row.push([board[i][j], j]);
}
row.sort((a, b) => b[0] - a[0]);
rowTop3.push(row.slice(0, Math.min(3, n)));
}
let maxSum = -Infinity;
// Try all combinations of 3 rows
for (let r1 = 0; r1 < m; r1++) {
for (let r2 = r1 + 1; r2 < m; r2++) {
for (let r3 = r2 + 1; r3 < m; r3++) {
// Try all combinations of columns from top 3 of each row
for (let [val1, c1] of rowTop3[r1]) {
for (let [val2, c2] of rowTop3[r2]) {
if (c2 === c1) continue;
for (let [val3, c3] of rowTop3[r3]) {
if (c3 === c1 || c3 === c2) continue;
maxSum = Math.max(maxSum, val1 + val2 + val3);
}
}
}
}
}
}
return maxSum;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(m × n log n + m²) | 排序每行需要O(n log n),枚举需要O(m²) |
| 空间复杂度 | O(m × n) | 存储每行的前3大值和候选列表 |