2015 ACM National Contest Romania - Round 1 G Por Costel and the Orchard(DP)

题目链接
题意

给你一个 n ∗ m n*m nm 的矩阵,每个节点有个权值,求一个权值和最大的联通块。
连通块还要满足集合不为空,单独行行内也连通

思路

d p [ 第 i 行 ] [ j 为 左 端 点 ] [ k 为 右 端 点 ] = 最 大 权 值 dp[第i行][j为左端点][k为右端点]=最大权值 dp[i][j][k]=
考虑三种转移(存在交集的情况),设上层转移而来区间为 [ q , p ] [q,p] [q,p]

  • q , j &lt; = p &lt; = k q,j&lt;=p&lt;=k q,j<=p<=k g [ 以 j 为 右 端 点 ] = 上 层 d p 前 缀 最 大 值 , g g [ j ] [ k ] 区 间 = g 最 大 值 g[以j为右端点]=上层dp前缀最大值,gg[j][k]区间=g最大值 g[j]=dpgg[j][k]=g
  • j &lt; = q &lt; = p , k j&lt;=q&lt;=p,k j<=q<=p,k f [ 以 j 为 左 端 点 ] = 上 层 d p 后 缀 最 大 值 , f f [ j ] [ k ] 区 间 = f 最 大 值 f[以j为左端点]=上层dp后缀最大值,ff[j][k]区间=f最大值 f[j]=dpff[j][k]=f
  • q &lt; = j &lt; = k &lt; = p q&lt;=j&lt;=k&lt;=p q<=j<=k<=p w [ j ] [ k ] = 上 层 d p 包 含 [ j , k ] 区 间 的 最 大 值 w[j][k]=上层dp包含[j,k]区间的最大值 w[j][k]=dp[j,k]

d p [ i ] [ j ] [ k ] = 区 间 和 + m a x ( g g [ j ] [ k ] , f f [ j ] [ k ] , w [ j ] [ k ] ) ; dp[i][j][k] = 区间和+max(gg[j][k],ff[j][k],w[j][k]); dp[i][j][k]=+max(gg[j][k],ff[j][k],w[j][k]);

最后还要满足集合不为空,那么可以将,每次dp值正常求,上层的转移本层的贡献值不低于0。

每个都可以 O ( n 2 ) O(n^2) O(n2)处理,dp需要滚动数组优化空间,区间和用前缀和求,复杂度O(n^3),详见代码。

NB群友直接预处理了包含某个点能选最大值,感觉帮我第三种情况改改也可以做到。。写麻烦了不管了(菜啊)。

代码
#include <bits/stdc++.h>
using namespace std;

template <class T> T mmax(T a, T b) { return max(a,b); }
template <class T, class ...R> T mmax(T a, R... b) { return max(a,mmax(b...)); }

int a[305][305], dp[2][305][305], g[305], gg[305][305], f[305], ff[305][305], w[305][305];

int main() {
    #ifdef ONLINE_JUDGE
    freopen("livada2.in","r",stdin);
    freopen("livada2.out","w",stdout);
    #endif
    int t;
    for(scanf("%d",&t); t; --t) {
        int n, m, cur = 1, ans = ~0x3f3f3f3f;
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) scanf("%d",&a[i][j]);

            cur ^= 1;
            memset(dp[cur],0,sizeof(dp[cur]));
            memset(g,0,sizeof(g));
            memset(gg,0,sizeof(gg));
            memset(f,0,sizeof(f));
            memset(ff,0,sizeof(ff));
            memset(w,0,sizeof(w));

            for(int j = 1; j <= m; ++j) {
                for(int k = 1; k <= j; ++k) g[j] = max(dp[cur^1][k][j], g[j]);
            }
            for(int j = 1; j <= m; ++j) {
                for(int k = j; k <= m; ++k) {
                    gg[j][k] = max(gg[j][k-1],g[k]);
                }
            }

            for(int j = m; j; --j) {
                for(int k = m; k >= j; --k) f[j] = max(f[j], dp[cur^1][j][k]);
            }
            for(int j = 1; j <= m; ++j) {
                for(int k = j; k <= m; ++k) {
                    ff[j][k] = max(ff[j][k-1],f[k]);
                }
            }

            for(int j = 1; j <= m; ++j) {
                for(int k = m; k >= j; --k) {
                    w[j][k] = mmax(w[j-1][k], w[j][k+1], dp[cur^1][j][k]);
                }
            }

            a[i][0] = 0;
            for(int j = 1; j <= m; ++j) a[i][j] += a[i][j-1];

            for(int j = 1; j <= m; ++j) {
                for(int k = j; k <= m; ++k) {
                    dp[cur][j][k] = a[i][k]-a[i][j-1]+mmax(gg[j][k],ff[j][k],w[j][k]);
                    ans = max(ans, dp[cur][j][k]);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值