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; }
注意:
调试时,先把思路顺一遍,再去调试.否则十分耗时耗力.
$By\ LIN452$
$2017.06.07$