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

题目大意

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

题解

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

容易想到dp,设(dp[i][j])表示考虑了前(i)行,且以第(i)行的第(j)个格子为右下角选取了一个(2 imes 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
",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
",Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/12334724.html