P2331 [SCOI2005]最大子矩阵(两列矩阵 框k个矩阵 dp)

原题: https://www.luogu.org/problemnew/show/P2331

题意:

n*m的矩阵,你选出其中k个不重叠子矩阵,使得这个k个子矩阵分值之和最大。子矩阵可以是空矩阵。

解析:

被空矩阵秀到了,是我线代不好还是出题人的问题。。。

这题有个特点,m最大为2,也就是说,选取的可能性为:选左边一条,右边一条,或是两格宽的一条。

m==1的部分就不说了

解法一:O( n 3 n^3 K)

自己想的,看了数据范围也没多想提起代码就上了。

dp[i][j][k]表示左边已经到i,右边到j,已经选择k个矩阵的最大值。

转移方程:

  1. 选左边: d p [ i + d ] [ j ] [ k + 1 ] = d p [ i ] [ j ] [ k ] + b e s t [ ] [ i + 1 ] [ i + d ] dp[i+d][j][k+1]=dp[i][j][k]+best[左][i+1][i+d]
  2. 选右边: d p [ i ] [ j + d ] [ k + 1 ] = d p [ i ] [ j ] [ k ] + b e s t [ ] [ j + 1 ] [ j + d ] dp[i][j+d][k+1]=dp[i][j][k]+best[右][j+1][j+d]
  3. 选两边: d p [ m a x ( i , j ) + d ] [ m a x ( i , j ) + d ] [ k + 1 ] = d p [ i ] [ j ] [ k ] + b e s t [ ] [ m a x ( i , j ) + 1 ] [ m a x ( i , j ) + d ] dp[max(i,j)+d][max(i,j)+d][k+1]=dp[i][j][k]+best[两边][max(i,j)+1][max(i,j)+d]

这个best数组是什么意思呢?

因为 i , j , k i,j,k 这个状态通过 i + d i+d 只能转移到 i + d , j , k + 1 i+d,j,k+1 这个状态,但是我下一个矩阵选择可能是从 i + d + 2 , j , k + 1 i+d+2,j,k+1 来的。
简言之,我在更新一个状态后,需要是二维偏序大于这个状态的状态也可以使用这个更新。

如果每次更新都往后一个一个更新太浪费时间了,所以记录best[l][r],表示从l到r这个区间选择一个子区间的最大值。
这样后序状态也可以使用这个更新了。

不要问我为什么不把“往后更新”换成“从前面更新”,我敲的时候也没有想到这茬啊,best这东西也是临时想的。。。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define debug(i) printf("# %d\n",i)

int a[109][3];
int s[3][109];
int best[3][109][109];
int dp[109][109][11];
const int inf=0xc0c0c0c0;
void init(int n,int m){
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            s[i][j]=s[i][j-1]+a[j][i];
        }
        for(int j=1;j<=n;j++){
            best[i][j][j]=a[j][i];
        }
        for(int l=2;l<=n;l++){
            for(int j=1;j+l-1<=n;j++){
                best[i][j][j+l-1]=max(best[i][j][j+l-2],max(best[i][j+1][j+l-1],s[i][j+l-1]-s[i][j-1]));
            }
        }
    }
    if(m==2){
        for(int i=1;i<=n;i++)best[0][i][i]=a[i][1]+a[i][2];

        for(int l=2;l<=n;l++){
            for(int j=1;j+l-1<=n;j++){
                best[0][j][j+l-1]=max(best[0][j][j+l-2],max(best[0][j+1][j+l-1],s[2][j+l-1]-s[2][j-1]+s[1][j+l-1]-s[1][j-1]));
            }
        }
    }
}

int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",a[i]+j);
        }
    }
    init(n,m);

//*********
    if(m==1){
        int ans=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<k;j++){
                for(int l=1;;l++){
                    if(i+l>n)break;
                    dp[0][i+l][j+1]=max(dp[0][i+l][j+1],dp[0][i][j]+best[1][i+1][i+l]);
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int t=0;t<=k;t++)
            ans=max(ans,dp[0][i][t]);
        }
        printf("%d\n",ans);
        return 0;
    }
//*********

    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            for(int l=1;;l++){
                if(i+l>n)break;
                for(int t=0;t<k;t++){
                    dp[i+l][j][t+1]=max(dp[i+l][j][t+1],dp[i][j][t]+best[1][i+1][i+l]);
                }
            }
            for(int l=1;;l++){
                if(j+l>n)break;
                for(int t=0;t<k;t++){
                    dp[i][j+l][t+1]=max(dp[i][j+l][t+1],dp[i][j][t]+best[2][j+1][j+l]);
                }
            }
            for(int l=1;;l++){
                int to=max(i,j);
                if(to+l>n)break;
                for(int t=0;t<k;t++){
                    dp[to+l][to+l][t+1]=max(dp[to+l][to+l][t+1],dp[i][j][t]+best[0][to+1][to+l]);
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            for(int t=0;t<=k;t++)
            ans=max(ans,dp[i][j][t]);
        }
    }
    printf("%d\n",ans);
    return 0;
}

解法二:O( n n K)

对于答案状态分析,每一行只有5种状态:00,10,01,1|1,11。最后两种:1|1代表两个格子在不同的两条矩阵种,11代表这行在一个两格宽的矩阵里。

想到这里,转移方程就再简单不过了。
dp[i][j][k]表示到第 i i 行,用 j j 个矩阵, i i 行状态为 k k 的最值

  1. d p [ i ] [ j ] [ 0 ] d p [ i 1 ] [ j ] [ 0 dp[i][j][0]:dp[i-1][j][0 ~ 4 ] 4]
  2. d p [ i ] [ j ] [ 1 ] m a x ( d p [ i 1 ] [ j ] [ 1 3 ] d p [ i 1 ] [ j 1 ] [ 0 2 4 ] ) + a [ i ] [ 0 ] dp[i][j][1]:max(dp[i-1][j][1、3]、dp[i-1][j-1][0、2、4])+a[i][0]
  3. d p [ i ] [ j ] [ 2 ] m a x ( d p [ i 1 ] [ j ] [ 2 3 ] d p [ i 1 ] [ j 1 ] [ 0 1 4 ] ) + a [ i ] [ 1 ] dp[i][j][2]:max(dp[i-1][j][2、3]、dp[i-1][j-1][0、1、4])+a[i][1]
  4. d p [ i ] [ j ] [ 3 ] m a x ( d p [ i 1 ] [ j ] [ 3 ] d p [ i 1 ] [ j 1 ] [ 1 2 ] d p [ i 1 ] [ j 2 ] [ 0 5 ] ) + a [ i ] [ 0 ] + a [ i ] [ 1 ] dp[i][j][3]:max(dp[i-1][j][3]、dp[i-1][j-1][1、2]、dp[i-1][j-2][0、5])+a[i][0]+a[i][1]
  5. d p [ i ] [ j ] [ 4 ] m a x ( d p [ i 1 ] [ j ] [ 4 ] d p [ i 1 ] [ j 1 ] [ 0 1 2 3 ] ) + a [ i ] [ 1 ] + a [ i ] [ 2 ] dp[i][j][4]:max(dp[i-1][j][4]、dp[i-1][j-1][0、1、2、3])+a[i][1]+a[i][2]
#include<bits/stdc++.h>
using namespace std;

int a[109][2];
int dp[109][11][7];
const int INF=0x3f3f3f3f;

int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",a[i]+j);
// 不存在状态一定没有存在状态优

    memset(dp,0xc0,sizeof dp);
    for(int i=0;i<=n;i++)dp[i][0][0]=0;

    if(m==1){
        int ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=k;j++){
                dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);
                dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0])+a[i][1];
                ans=max(ans,max(dp[i][j][0],dp[i][j][1]));
            }
        }
        return 0*printf("%d\n",ans);
    }


    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=k;j++){
            int add;
            dp[i][j][0]=max(dp[i-1][j][0],max(dp[i-1][j][1],max(dp[i-1][j][2],max(dp[i-1][j][3],dp[i-1][j][4]))));

            add=a[i][1];
            dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j][3]);
            dp[i][j][1]=max(dp[i][j][1],max(dp[i-1][j-1][0],max(dp[i-1][j-1][2],dp[i-1][j-1][4])));
            dp[i][j][1]+=add;

            add=a[i][2];
            dp[i][j][2]=max(dp[i-1][j][2],dp[i-1][j][3]);
            dp[i][j][2]=max(dp[i][j][2],max(dp[i-1][j-1][0],max(dp[i-1][j-1][1],dp[i-1][j-1][4])));
            dp[i][j][2]+=add;

            add=a[i][1]+a[i][2];
            dp[i][j][3]=dp[i-1][j][3];
            dp[i][j][3]=max(dp[i][j][3],max(dp[i-1][j-1][1],dp[i-1][j-1][2]));
            if(j>1)dp[i][j][3]=max(dp[i][j][3],max(dp[i-1][j-2][0],dp[i-1][j-2][4]));
            dp[i][j][3]+=add;

            dp[i][j][4]=dp[i-1][j][4];
            dp[i][j][4]=max(dp[i][j][4],max(dp[i-1][j-1][0],max(dp[i-1][j-1][1],max(dp[i-1][j-1][2],dp[i-1][j-1][3]))));
            dp[i][j][4]+=add;
        }
    }
    for(int i=0;i<=k;i++)
        for(int j=0;j<=4;j++)
            ans=max(ans,dp[n][i][j]);
    return 0*printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/87524824