2015 ACM National Contest Romania G - Por Costel and the Orchard Gym - 100923G (好难的dp)

问题

给一个NxM的矩阵,找一个联通块,让这个联通块的权值最大。
有这么几个要求:
1.每一行不能间断地取点,也就是每一行必须取一段连续的区间。
2.下一行如果取了那么与上一行必须有相交的部分,也就是要联通
3.不能不取,至少取一格
( 1<=T<=4,1<=n,m<=300 ,-1e4<=a[i][j]<=1e4)

思路

首先,我们很容易想到一个dp
dp[i][j][k]表示到第i行,区间为j-k与上面联通的最大价值。
与区间[j][k]联通也就是上一行左右端点有至少有一个在[j][k]之内
这个代码比较好写,如下

for(int i=2;i<=n;i++){
            for(int j=1;j<=m;j++){
                for(int k=j;k<=m;k++){
                    for(int qq=j;qq<=k;qq++){
                        if(dp[i][j][k]<pre[i-1][qq]+sum[i][k]-sum[i][j-1]){
                            dp[i][j][k]=pre[i-1][qq]+sum[i][k]-sum[i][j-1];
                            pre[i][j]=max(pre[i][j],dp[i][j][k]);
                            suf[i][k]=max(suf[i][k],dp[i][j][k]);
                            ans=max(ans,dp[i][j][k]);
                        }
                        if(dp[i][j][k]<suf[i-1][qq]+sum[i][k]-sum[i][j-1]){
                            dp[i][j][k]=suf[i-1][qq]+sum[i][k]-sum[i][j-1];
                            pre[i][j]=max(pre[i][j],dp[i][j][k]);
                            suf[i][k]=max(suf[i][k],dp[i][j][k]);
                              ans=max(ans,dp[i][j][k]);
                        }
                    }
                    
                }
            }
        }

解释一下,pre[i][j]表示第i行,以j为右端点能联通的最大值
suf[i][j]表示第i行以j为左端点能联通的最大值.sum[i][j]表示第i行的前缀和,之前已经先处理了一下第一行,所以这段代码从第二行开始dp
我们分析一下这段代码,发现这是一个n^4的dp,对于300的数据是过不了的,考虑一下优化。
榘蒻想了很久也不会做,于是求助大佬,愤怒学习了一波代码之后,得出了以下结论。
我们只要处理出上一行对于每一个点,他能与上面联通的最大价值就ok,具体看看代码,写在注释里面了

#include <iostream>
#include<algorithm>
#include "cstring"
using namespace std;
#define ll long long
#define maxn 305
#define inf 0x3f3f3f3f
#define mod 998244353
int dp[maxn][maxn][2];
int suf[maxn];
int sum[maxn],a[maxn][maxn];
int main(){
    int t;
    freopen("livada2.in","r",stdin);
    freopen("livada2.out","w",stdout);
    scanf("%d",&t);
    while(t--){
        int n,m;
        int ans=-inf;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
                //sum[j]=sum[j-1]+a[i][j];
            }
        }
        sum[0]=0;
        for(int i=1;i<=m;i++)sum[i]=sum[i-1]+a[1][i];
        int cur=0;
        // 先搞第一行
        for(int i=1;i<=m;i++){
            for(int j=i;j<=m;j++){
                dp[i][j][cur]=sum[j]-sum[i-1];
                ans=max(ans,dp[i][j][cur]);
            }
        }
        cur^=1;
        for(int i=2;i<=n;i++){
            //先把当前一行的东西清空,是上两行的东西,没有用
            sum[0]=0;
            for(int j=1;j<=m;j++){
                sum[j]=sum[j-1]+a[i][j];
                suf[j]=-inf;
                for(int k=1;k<=m;k++){
                    dp[j][k][cur]=-inf;
                }
            }
            //这一段是优化的关键
            for(int j=1;j<=m;j++){
                int x=-inf;
                for(int k=m;k>=j;k--){    //这边从后往前搞
                    x=max(x,dp[j][k][cur^1]);  //这句话的意思是找到上一行区间为[j,k:m>j]的联通块的最大价值,从后往前搞的意义在于[j][k-2]这个区间包含于[j][k-1]这个区间
                    suf[k]=max(x,suf[k]);  //suf[k]表示上一行能包含k这个点的区间往上的最大联通块的权值
                }
            }
            for(int j=1;j<=m;j++){
                int x=0;
                for(int k=j;k<=m;k++){
                    x=max(x,suf[k]); //在区间j-k中,x是上一行[j][k]中包含某一段的联通块最大值
                    dp[j][k][cur]=x+sum[k]-sum[j-1];// 加上这一段的区间和,ans求个max
                    ans=max(ans,dp[j][k][cur]);
                }
            }
            cur^=1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
 /*
  input
  1
 3 4
 5 -3 0 0
 -2 3 3 4
 -7 -6 4 -5
  output
  17
*/

这题感觉好难- 。- 还是要多复习复习。

全部评论

相关推荐

零零幺零零幺:至少再做一个项目,然后猛投小厂,不然有点难
点赞 评论 收藏
分享
03-08 18:11
门头沟学院 Java
Java抽象小篮子:海投就完事了,简历没什么问题,最大问题是学历
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
正在热议
更多
# 长得好看会提高面试通过率吗? #
4609次浏览 48人参与
# 离家近房租贵VS离家远但房租低,怎么选 #
16917次浏览 137人参与
# 米连集团26产品管培生项目 #
7424次浏览 228人参与
# 沪漂/北漂你觉得哪个更苦? #
1646次浏览 41人参与
# 你的实习产出是真实的还是包装的? #
3261次浏览 54人参与
# 春招至今,你的战绩如何? #
16231次浏览 147人参与
# 巨人网络春招 #
11559次浏览 230人参与
# HR最不可信的一句话是__ #
1118次浏览 33人参与
# AI面会问哪些问题? #
976次浏览 24人参与
# 你做过最难的笔试是哪家公司 #
1319次浏览 23人参与
# AI时代,哪个岗位还有“活路” #
2956次浏览 53人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152954次浏览 889人参与
# 简历第一个项目做什么 #
32209次浏览 364人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
8032次浏览 43人参与
# XX请雇我工作 #
51165次浏览 171人参与
# 简历中的项目经历要怎么写? #
311176次浏览 4273人参与
# 投格力的你,拿到offer了吗? #
178395次浏览 891人参与
# 你最满意的offer薪资是哪家公司? #
77017次浏览 375人参与
# AI时代,哪些岗位最容易被淘汰 #
64881次浏览 895人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187657次浏览 1123人参与
# 你怎么看待AI面试 #
180898次浏览 1320人参与
# 正在春招的你,也参与了去年秋招吗? #
364423次浏览 2642人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务