[线段树优化dp] Codeforces 1304F2 Animal Observation (hard version)

题目大意

给定一个\(N \times M(N \leq 50,M \leq 20000)\)的矩阵,给定\(K\leq M\),要求以每行的某个点为左上角选取一个\(2 \times K\)的子矩阵,使得所有选出的子矩阵覆盖的值之和最大,输出这个最大值。如下图。

[线段树优化dp] Codeforces 1304F2 Animal Observation (hard version)

题解

这道题的Easy版本和Hard版本唯一的区别就是\(K\)的大小不一样,Easy版本\(K\)是20,Hard版本\(K\)是20000。

容易想到dp,设\(dp[i][j]\)表示考虑了前\(i\)行,且以第\(i\)行的第\(j\)个格子为右下角选取了一个\(2 \times K\)的矩阵时的最大价值。

那么\(dp[i][j]=max(dp[i-1][k]-S)\),\(S\)表示本次选的以\((i,j)\)为右下角的矩形和上一次选的以\((i-1,k)\)为右下角的矩形的相交部分的价值,最终的答案就是\(min\{dp[N][j]\}\)。

容易想到,对于两个矩形没有相交的情况,可以维护\(dp[i][j]\)的前缀最大值和后缀最大值,然后状态转移是\(O(1)\)的。

对于两个矩形相交的情况,对于\(Easy\)版本,因为\(K\)只有20,我们可以去暴力维护。但对于Hard版本,复杂度太高。

对于\(dp[i][j]\),我们是固定了\(i\),然后从从小到大枚举\(j\)去\(dp\)的。考虑\(dp[i][j-1]\)和\(dp[i][j]\)之间的关系。

\(dp[i][j-1]\)选取的是左上角为\((i-1,j-K)\),右下角为\((i,j-1)\)的矩形,\(dp[i][j]\)选取的是左上角为\((i-1,j-K+1)\),右下角为\((i,j)\)的矩形,相当于选取的矩形往右移动了1格。那么对于右下角在第\(i-1\)行\([j-K,j-1]\)范围内的矩形,在当前选取的矩形往右移动一格时,其\(dp\)值要加上\(Matrix[i-1][j-K]\),对于右下角在第\(i-1\)行\([j,j+K-1]\)范围内的矩形,在当前选取的矩形往右移动一格时,其\(dp\)值要减去\(Matrix[i-1][j]\)。

即要维护区间加、区间最大值,可以用线段树做。时间复杂度\(O(NMlogM)\)。

Codeforces 1304F1 Easy版代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

int Matrix[53][20010],Sum[53][20010],dp[53][20010];
int Pre[53][20010],Suf[53][20010];
int N,M,K;

inline int Calc(int x,int y){
    int Res=0;
    for(RG j=max(y-K+1,K);j<=y;++j){
        int temp=Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K]
                +dp[x-1][j]-(Sum[x-1][j]-Sum[x-1][y-K]-Sum[x-2][j]+Sum[x-2][y-K]);
        Res=max(Res,temp);
    }
    for(RG j=y+1;j<=min(M,y+K-1);++j){
        int temp=Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K]
                +dp[x-1][j]-(Sum[x-1][y]-Sum[x-1][j-K]-Sum[x-2][y]+Sum[x-2][j-K]);
        Res=max(Res,temp);
    }
    return Res;
}

int main(){
    Read(N);Read(M);Read(K);
    for(RG i=1;i<=N;++i)
        for(RG j=1;j<=M;++j)
            Read(Matrix[N-i+2][j]);
    for(RG i=1;i<=N+1;++i)
        for(RG j=1;j<=M;++j)
            Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+Matrix[i][j];
    for(RG i=K;i<=M;++i)
        dp[2][i]=Sum[2][i]-Sum[0][i]-Sum[2][i-K]+Sum[0][i-K];
    for(RG j=1;j<=M;++j){
        Pre[2][j]=max(Pre[2][j-1],dp[2][j]);
        Suf[2][M-j+1]=max(Suf[2][M-j+2],dp[2][M-j+1]);
    }
    for(RG i=3;i<=N+1;++i){
        for(RG j=K;j<=M;++j){
            dp[i][j]=Pre[i-1][j-K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K];
            if(j+K<=M) dp[i][j]=max(dp[i][j],Suf[i-1][j+K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K]);
            dp[i][j]=max(dp[i][j],Calc(i,j));
        }
        for(RG j=1;j<=M;++j){
            Pre[i][j]=max(Pre[i][j-1],dp[i][j]);
            Suf[i][M-j+1]=max(Suf[i][M-j+2],dp[i][M-j+1]);
        }
    }
    int Ans=0;
    for(RG i=K;i<=M;++i)
        Ans=max(Ans,dp[N+1][i]);
    printf("%d\n",Ans);
    return 0;
}

Codeforces 1304F2 Hard版代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

int Matrix[53][20010],Sum[53][20010],dp[53][20010];
int Pre[53][20010],Suf[53][20010];
int N,M,K;

struct SegTreeNode{int Value,Lazytag;};
SegTreeNode SegTree[80010];
int Data[20010];

void Build_SegTree(int Root,int L,int R){
    SegTree[Root].Lazytag=0;
    if(L==R){SegTree[Root].Value=Data[L];return;}
    int mid=(L+R)>>1;
    Build_SegTree(Root<<1,L,mid);
    Build_SegTree(Root<<1|1,mid+1,R);
    SegTree[Root].Value=max(SegTree[Root<<1].Value,SegTree[Root<<1|1].Value);
    return;
}

inline void Push_Down(int Root){
    if(!SegTree[Root].Lazytag) return;
    int Val=SegTree[Root].Lazytag;
    SegTree[Root].Lazytag=0;
    SegTree[Root<<1].Value+=Val;
    SegTree[Root<<1].Lazytag+=Val;
    SegTree[Root<<1|1].Value+=Val;
    SegTree[Root<<1|1].Lazytag+=Val;
    return;
}

void Update(int Root,int L,int R,int UL,int UR,int Add){
    if(R<UL || UR<L) return;
    if(UL<=L && R<=UR){
        SegTree[Root].Value+=Add;
        SegTree[Root].Lazytag+=Add;
        return;
    }
    Push_Down(Root);
    int mid=(L+R)>>1;
    Update(Root<<1,L,mid,UL,UR,Add);
    Update(Root<<1|1,mid+1,R,UL,UR,Add);
    SegTree[Root].Value=max(SegTree[Root<<1].Value,SegTree[Root<<1|1].Value);
    return;
}

int Query(int Root,int L,int R,int QL,int QR){
    if(R<QL || QR<L) return 0;
    if(QL<=L && R<=QR) return SegTree[Root].Value;
    Push_Down(Root);
    int mid=(L+R)>>1;
    return max(Query(Root<<1,L,mid,QL,QR),Query(Root<<1|1,mid+1,R,QL,QR));
}

inline int Calc(int x,int y){
    if(y!=K) Update(1,1,M,max(y-K,K),y-1,Matrix[x-1][y-K]);
    if(y!=K) Update(1,1,M,y,min(y+K-1,M),-Matrix[x-1][y]);
    return Query(1,1,M,y-K+1,min(y+K-1,M))+Sum[x][y]-Sum[x-2][y]-Sum[x][y-K]+Sum[x-2][y-K];
}

int main(){
    Read(N);Read(M);Read(K);
    for(RG i=1;i<=N;++i)
        for(RG j=1;j<=M;++j)
            Read(Matrix[N-i+2][j]);
    for(RG i=1;i<=N+1;++i)
        for(RG j=1;j<=M;++j)
            Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+Matrix[i][j];
    for(RG i=K;i<=M;++i)
        dp[2][i]=Sum[2][i]-Sum[0][i]-Sum[2][i-K]+Sum[0][i-K];
    for(RG j=1;j<=M;++j){
        Pre[2][j]=max(Pre[2][j-1],dp[2][j]);
        Suf[2][M-j+1]=max(Suf[2][M-j+2],dp[2][M-j+1]);
        if(K<=j && j<=2*K) Data[j]=dp[2][j]-(Sum[2][K]-Sum[2][j-K]-Sum[1][K]+Sum[1][j-K]);
        else Data[j]=dp[2][j];
    }
    for(RG i=3;i<=N+1;++i){
        Build_SegTree(1,1,M);
        for(RG j=K;j<=M;++j){
            dp[i][j]=Pre[i-1][j-K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K];
            if(j+K<=M) dp[i][j]=max(dp[i][j],Suf[i-1][j+K]+Sum[i][j]-Sum[i-2][j]-Sum[i][j-K]+Sum[i-2][j-K]);
            dp[i][j]=max(dp[i][j],Calc(i,j));
        }
        for(RG j=1;j<=M;++j){
            Pre[i][j]=max(Pre[i][j-1],dp[i][j]);
            Suf[i][M-j+1]=max(Suf[i][M-j+2],dp[i][M-j+1]);
            if(K<=j && j<=2*K) Data[j]=dp[i][j]-(Sum[i][K]-Sum[i][j-K]-Sum[i-1][K]+Sum[i-1][j-K]);
            else Data[j]=dp[i][j];
        }
    }
    int Ans=0;
    for(RG i=K;i<=M;++i)
        Ans=max(Ans,dp[N+1][i]);
    printf("%d\n",Ans);
    return 0;
}
上一篇:机器学习-决策树之ID3算法


下一篇:2021.06.15