NOI2013 书法家

题意:

有一个$n*m$的矩阵.现在要在矩阵中写三个字母"N","O","I",求出写下合法字母的格子的权值和的最大值.

书写规则如下:

$n<=150,m<=500,-200<=A[i][j]<=200$

题解:

此题的$n,m$范围较小,可以考虑DP.

分段考虑.

字母"O"和"I"都可以用简单的前缀和优化在$O(n^{2}*m)$内解决.

现在考虑"N",把N分为三部分,两边的"一竖"和中间递减的一段.

每一段都可以用前缀和进行优化.

思路很简单,就是操作很复杂.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=155,M=505,oo=1e9;
int w[N][M],n,m,DPn[M],DPo[M],DPi[M];
int dp[N][N][M],f[N][N][M],sum[N][N][M],s[N][N][M],lft[N][N][M],val[N][N][M];///可以开9~10个 
inline void Max(int &x,int y){if(x<y)x=y;}
void solveN(){
    int i,j,k,b,t,B,T;
    for(i=1;i<=m;i++){
        for(b=1;b<=n;b++)
            for(t=b;t<=n;t++)sum[b][t][i]=sum[b][t-1][i]+w[t][i];
        DPn[i]=-oo;
    }
    for(b=1;b<=n;b++){
        for(t=b;t<=n;t++){
            for(i=1;i<=m;i++){
                s[b][t][i]=s[b][t][i-1]+sum[b][t][i];//第i列[b,t]
                lft[b][t][i]=sum[b][t][i]+max(0,lft[b][t][i-1]);
            }
        }
    }
    //得到第二列的dp值 
    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)    
            for(k=0;k<=m;k++)dp[i][j][k]=-oo;
    for(B=1;B<=n;B++){
        for(i=1;i<=m;i++)f[B][n][i]=-oo;
        for(T=n-1;T>=B;T--){//第二行最后一个不可能是n 对! 
            int mx=-oo;
            for(i=1;i<=m;i++){
                dp[B][T][i]=mx+s[B][T][i];//Max{s[B][T][i]-s[B][T][j]+f[B][T][j]}//j<i求出前缀f-s的最值 
                f[B][T][i]=max(f[B][T+1][i],lft[B][T+1][i]);  
                Max(mx,f[B][T][i]-s[B][T][i]);//注意f还没求呢 
            }
        } 
    }
    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)
            for(k=0;k<=m;k++)f[i][j][k]=val[i][j][k]=-oo;
    for(B=1;B<=n;B++){
        for(i=2;i<=m;i++)
            f[B][B-1][i]=val[B-1][B-1][i];
        for(T=B;T<=n;T++){
            int mx=-oo;
            for(i=2;i<=m;i++){//从第三列开始的dp值 
                Max(dp[B][T][i],mx+s[B][T][i]);//dp[B][T][i]=s[B][T][i]+Max{f[B][T][j]-s[B][T][j]}.j<i
                val[B][T][i]=max(val[B-1][T][i],dp[B][T][i]);
                f[B][T][i]=max(f[B][T-1][i],val[B][T][i]);
                Max(mx,f[B][T][i]-s[B][T][i]);
                
            }
        }
    } 
    for(i=0;i<=n;i++)
        for(j=0;j<=n;j++)
            for(k=0;k<=m;k++)f[i][j][k]=-oo;
    for(B=n-1;B>=1;B--){ 
        for(T=B+1;T<=n;T++){
            int mx=-oo;
            for(i=1;i<=m;i++){
                Max(DPn[i],mx+s[B][T][i]);//=s[B][T][i]+Max{f[B][T][j]-}这里用赋值  
                f[B][T][i]=max(f[B+1][T][i],dp[B+1][T][i]);
                Max(mx,f[B][T][i]-s[B][T][i]);
            }
        }
    }
    //答案是-5才对 
    for(i=1;i<=m;i++)Max(DPn[i],DPn[i-1]);
} 
int ss[N][M];//第i行第前j个 
void solveO(){
    int i,j,k,B,T;
     for(j=1;j<=m;j++){
         DPo[j]=-oo;
        for(i=1;i<=n;i++)
         ss[i][j]=ss[i][j-1]+w[i][j]; 
    }
    for(B=1;B<=n;B++){//至少是3 
        for(T=B+2;T<=n;T++){
            int mx=-oo;
            for(i=6;i<=m;i++){//至少是6 
                Max(DPo[i],mx+sum[B+1][T-1][i]+ss[B][i]+ss[T][i]);//j<i-1
                Max(mx,-ss[B][i-2]-ss[T][i-2]+sum[B+1][T-1][i-1]+DPn[i-3]);
            }
        }
    } 
    for(i=1;i<=m;i++)Max(DPo[i],DPo[i-1]);
}
int rght[N][N][M];
void solveI(){
    int i,j,k,B,T;
    for(j=m;j>=1;j--){
        for(i=1;i<=n;i++){
            for(k=i;k<=n;k++)
                rght[i][k][j]=w[i][j]+w[k][j]+max(rght[i][k][j+1],0);
        }
        DPi[j]=-oo;
    }
    for(i=1;i<=n;i++){
        for(k=i+2;k<=n;k++){
            lft[i][k][0]=lft[i][k][1]=-oo;
            for(j=2;j<=m;j++){
                lft[i][k][j]=w[i][j]+w[k][j]+max(lft[i][k][j-1],DPo[j-2]);
            }
        }
    }
    for(B=1;B<=n;B++){
        for(T=B+2;T<=n;T++){
            int mx=-oo;
            for(i=8;i<m;i++){
                Max(DPi[i],mx+s[B][T][i]+rght[B][T][i+1]);
                Max(mx,lft[B][T][i]-s[B][T][i]);//ss[B][i]-ss[B][j-1]+ss[T][i]-ss[B][j-1] +DPo[j-2] j<
            }
        }
    }
    for(i=1;i<=m;i++)Max(DPi[i],DPi[i-1]);
}
int main(){ 
    scanf("%d %d",&n,&m);
    int i,j,k;
    DPn[0]=DPo[0]=DPi[0]=-oo;
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++)
            scanf("%d",&w[i][j]);
    }
    solveN();
    solveO();
    solveI();
    printf("%d\n",DPi[m]);
    return 0;
    
}
View Code

注意:

调试时,先把思路顺一遍,再去调试.否则十分耗时耗力.

$By\ LIN452$

$2017.06.07$

posted @ 2017-06-06 18:33  LIN452  阅读(25)  评论(0编辑  收藏  举报