题目链接:
最大加权矩形 - 洛谷https://www.luogu.com.cn/problem/P1719
思路:
法1:二维前缀和,枚举所有矩形的暴力法,O(N^4)
// n<=120,可以用n^4的暴力法
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 125;
int n; //边长
int mp[maxn][maxn]; //记录矩形的二维前缀和
signed main(){
cin >> n;
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
cin >> mp[i][j];
mp[i][j] += (mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1]); //计算二维前缀和
}
}
int ans = -(1<<30);
//枚举左上角和右下角坐标
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
for(int x=i; x<=n; x++){
for(int y=j; y<=n; y++){
ans = max(ans, mp[x][y]-mp[i-1][y]-mp[x][j-1]+mp[i-1][j-1]);
}
}
}
}
cout << ans << '\n';
}
法2:在列方向上用一维前缀和,压缩成一维(变成只有一行) ,然后dp递推,思路类似leetcode 53.最大子序和,O(N^3)复杂度。
// n^3的动态规划法
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 125;
int n; //边长
int ans = -(1<<30);
int mp[maxn][maxn]; //记录矩形
int tmp[maxn]; //tmp[k]表示[i,j]行范围内第k列的前缀和
int dp[maxn];
void getDp(){
memset(dp, 0, sizeof(dp));
for(int i=1; i<=n; i++){
dp[i] = max(tmp[i], dp[i-1]+tmp[i]); //只有当前列,或者加上上前面val最大的矩形
ans = max(ans, dp[i]);
}
}
signed main(){
cin >> n;
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
cin >> mp[i][j];
}
}
for(int i=1; i<=n; i++){ //枚举起始行
memset(tmp, 0, sizeof(tmp)); //重置前缀数组
for(int j=i; j<=n; j++){ //枚举末尾行
for(int k=1; k<=n; k++) //枚举列
tmp[k] += mp[j][k];
getDp();
}
}
cout << ans << '\n';
}