难度:困难。
标签:队列,二分查找,动态规划。
动态规划(暴力版)
使用动态规划做,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示以
m
a
t
r
i
x
[
i
]
[
j
]
matrix[i][j]
matrix[i][j]开始的矩阵的和,代码中step_i和step_j表示当前矩阵的大小。
p
r
e
d
p
[
i
]
[
j
]
predp[i][j]
predp[i][j]表示上一个step_j=1时的dp。在
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]中i或j递增时,加相应的
p
r
e
d
p
[
]
[
]
predp[][]
predp[][]的值即可,具体思路看代码。
提交后,最后一个测试用例超出时间限制(27/27)
超时代码:
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> dp(m, vector<int>(n));
vector<vector<int>> pre_dp(m, vector<int>(n));
int res = -100000;
for(int step_i = 1; step_i <= m; ++step_i){
for(int step_j = 1; step_j <= n; ++step_j){
for(int i = 0; i <= m - step_i; ++i){
for(int j = 0; j <= n - step_j; ++j){
if(step_i == 1 && step_j == 1){
dp[i][j] = matrix[i][j];
pre_dp[i][j] = dp[i][j];
}
else if(step_j == 1){
dp[i][j] = pre_dp[i][j] + matrix[i + step_i - 1][j];
pre_dp[i][j] = dp[i][j];
}
else{
dp[i][j] += pre_dp[i][j + step_j - 1];
}
if(dp[i][j] <= k)res = max(res, dp[i][j]);
}
}
/*
cout << "------------------------------------------" << endl;
cout << step_i << " " << step_j << endl << endl;
for(int i = 0; i <= m - step_i; ++i){
for(int j = 0; j <= n - step_j; ++j)cout << dp[i][j] << " ";
cout << endl;
}
cout << "pre_dp" << endl;
for(int i = 0; i <= m - step_i; ++i){
for(int j = 0; j < n; ++j)cout << pre_dp[i][j] << " ";
cout << endl;
}*/
}
}
return res;
}
};
把
v
e
c
t
o
r
vector
vector改为
i
n
t
[
]
[
]
int[][]
int[][],然后过了。
正确解法:
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
int dp[105][105];
int pre_dp[105][105];
// vector<vector<int>> dp(m, vector<int>(n));
// vector<vector<int>> pre_dp(m, vector<int>(n));
int res = -100000;
for(int step_i = 1; step_i <= m; ++step_i){
for(int step_j = 1; step_j <= n; ++step_j){
for(int i = 0; i <= m - step_i; ++i){
for(int j = 0; j <= n - step_j; ++j){
if(step_i == 1 && step_j == 1){
dp[i][j] = matrix[i][j];
pre_dp[i][j] = dp[i][j];
}
else if(step_j == 1){
dp[i][j] = pre_dp[i][j] + matrix[i + step_i - 1][j];
pre_dp[i][j] = dp[i][j];
}
else{
dp[i][j] += pre_dp[i][j + step_j - 1];
}
if(dp[i][j] <= k)res = max(res, dp[i][j]);
}
}
}
}
return res;
}
};
结果:
加上27行的代码:
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
int dp[105][105];
int pre_dp[105][105];
// vector<vector<int>> dp(m, vector<int>(n));
// vector<vector<int>> pre_dp(m, vector<int>(n));
int res = -100000;
for(int step_i = 1; step_i <= m; ++step_i){
for(int step_j = 1; step_j <= n; ++step_j){
for(int i = 0; i <= m - step_i; ++i){
for(int j = 0; j <= n - step_j; ++j){
if(step_i == 1 && step_j == 1){
dp[i][j] = matrix[i][j];
pre_dp[i][j] = dp[i][j];
}
else if(step_j == 1){
dp[i][j] = pre_dp[i][j] + matrix[i + step_i - 1][j];
pre_dp[i][j] = dp[i][j];
}
else{
dp[i][j] += pre_dp[i][j + step_j - 1];
}
if(dp[i][j] <= k){
res = max(res, dp[i][j]);
if(res == k)return res;
}
}
}
}
}
return res;
}
};
结果:
动态规划(枚举+集合)
官方题解解法。
枚举matrix的上下边界,计算每一列元素的和,将二维问题转换位一维问题。
代码中,
i
i
i和
j
j
j分别表示上边界和下边界,
s
u
m
[
c
]
sum[c]
sum[c]表示
i
j
ij
ij范围内第
c
c
c列的元素总和。
接下来,计算sum的前缀和,并将前缀和添加到set中,
s
[
j
]
−
s
[
i
]
s[j]-s[i]
s[j]−s[i]即表示从i到j的元素总和,由于这个值要小于等于k,因此在set中查找第一个键值不小于
s
[
j
]
−
k
s[j] - k
s[j]−k的元素,若找到了这个元素,这个元素恰好等于
s
[
j
]
−
k
s[j] - k
s[j]−k,则说明结果为k,其他情况的结果为
s
−
∗
l
b
s - *lb
s−∗lb。
若没找到,则说明枚举的当前情况没有和小于k的矩阵。
正确解法:
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
int res = -100000;
for(int i = 0; i < m; ++i){ //枚举上边界
vector<int> sum(n);
for(int j = i; j < m; ++j){ // 枚举下边界
for(int c = 0; c < n; ++c){
sum[c] += matrix[j][c];
}
set<int> sumSet{0};
int s = 0;
for(int v: sum){
s += v;
auto lb = sumSet.lower_bound(s - k);
if(lb != sumSet.end()){
res = max(res, s - *lb);
}
sumSet.insert(s);
}
}
}
return res;
}
};
结果:
可在第19行后加一句:
if(res == k)return res;
结果为