100923G-Por Costel and the Orchard

题意:给你一个nm(300300)的矩阵,每个点都有一个权值-1e4-1e4,求最大联通块的权值。
思路:
dp其实很好想,dpxij表示第x行i-j区间所联通0-x-1行的最大权值。
那么压缩到n^3我们需要做几件事情:

  1. 在n^2的时间内求出0-x-1行的与ij的最大权值(预处理+均摊复杂度+dp)
  2. 滚动数组
    不好想的是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;
}
posted @ 2019-08-13 15:38  AlexPanda  阅读(132)  评论(0编辑  收藏  举报