题意:给你一个nm(300300)的矩阵,每个点都有一个权值-1e4-1e4,求最大联通块的权值。
思路:
dp其实很好想,dpxij表示第x行i-j区间所联通0-x-1行的最大权值。
那么压缩到n^3我们需要做几件事情:
- 在n^2的时间内求出0-x-1行的与ij的最大权值(预处理+均摊复杂度+dp)
- 滚动数组
不好想的是n^2预处理,类似dp中的dp
不如想想我们n^2可以做什么事情:a. 前缀和求出每个区间的权值和。b. 每个左端点向右所联通的最大值。
那么我们利用b,有规律的枚举区间使得该区间内所有点都被更新,那么外层枚举区间左端点,从1-m;内层枚举区间右端点从m-1。这样我们每次取一个(i,右端点)的最大值,每次更新右端点的最大值,最终即可得到该点所联通的最大值。
得到这个之后我们再有规律的枚举新的一行的区间,既可得到答案。
总结:
这种预处理是第一次见,不难但是确实不好想出来。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(ll i=0;i<n;i++)
#define for1(i,n) for(ll i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const ll maxn = 350;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll sum[maxn],dp[2][maxn][maxn],gg[maxn];
int main(){
#ifdef ONLINE_JUDGE
freopen("livada2.in","r",stdin);
freopen("livada2.out","w",stdout);
#endif
IO;
ll t;cin>>t;
while(t--){
ll n,m;cin>>n>>m;
vector<vector<ll> >a((n+1),vector<ll>(m+1));
for1(i,n) for1(j,m) cin>>a[i][j];
ll cur = 0,ans = -inf;
for1(j,m) sum[j] = sum[j-1]+a[1][j];
for1(i,m){
ll x = 0;
for(ll j = i;j<=m;j++){
dp[cur][i][j] = sum[j]-sum[i-1];
ans = max(ans,dp[cur][i][j]);
}
}
cur^=1;
for(ll i =2;i<=n;i++){
for1(j,m)for1(k,m) dp[cur][j][k] = -inf;
for1(j,m) gg[j] = -inf;
sum[0] = 0;
for1(j,m) sum[j] = sum[j-1]+a[i][j];
ll x = -inf;
for1(j,m){
ll x = -inf;
for(ll k = m;k>=j;k--){
x = max(x,dp[cur^1][j][k]);
gg[k] = max(gg[k],x);
}
}
for1(j,m){
ll x = 0;
for(ll k = j;k<=m;k++){
x = max(x,gg[k]);
dp[cur][j][k] = x + sum[k]-sum[j-1];
ans = max(dp[cur][j][k],ans);
//cerr<<dp[cur][j][k]<<' ';
}
//cerr<<'\n';
}
cur^=1;
}
cout << ans <<'\n';
}
return 0;
}