容易发现,单独考虑每一行的取数方案与其他行互不影响,所以我们可以通过单独考虑每一行最后累加答案进行求解。
表示消除当前行 的最大得分,考虑每一个数合并到左区间或者右区间得到如下的状态转移方程:
根据数据范围 可知最后的结果达到了 的级别,所以我们要用高精度。
#include <bits/stdc++.h>
#define For(I,X,Y) for(int I=(X);I<=(Y);I++)
#define ForDown(I,X,Y) for(int I=(X);I>=(Y);I--)
using namespace std;
inline int Read(){
int X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
const int Max=100;
int N,M,Num[Max][Max];
#define Set(A) memset(A,0,sizeof(A))
struct Node{
int Len,W[Max];
void Print(){
ForDown(I,Len,1) printf("%d",W[I]);
}
}Ans,DP[Max][Max];
Node Add(Node A,int B){
A.W[1]+=B;
For(I,1,85) A.W[I]+=A.W[I-1]/10,A.W[I-1]%=10;
ForDown(I,85,1) if(A.W[I]!=0) {A.Len=I;break;}
return A;
}
Node Mul(Node A,int B){
For(I,1,A.Len) A.W[I]*=B;
For(I,1,85) A.W[I]+=A.W[I-1]/10,A.W[I-1]%=10;
ForDown(I,85,1) if(A.W[I]!=0) {A.Len=I;break;}
return A;
}
void Update(Node &A,Node B,Node C){
if(B.Len>C.Len) {A=B;return;}
if(B.Len<C.Len) {A=C;return;}
ForDown(I,B.Len,1)
if(B.W[I]>C.W[I]) {A=B;return;}
else if(B.W[I]<C.W[I]) {A=C;return;}
A=B;
}
void GetAns(Node &A,Node B){
For(I,1,max(A.Len,B.Len)) A.W[I]+=B.W[I];
For(I,1,85) A.W[I]+=A.W[I-1]/10,A.W[I-1]%=10;
ForDown(I,85,1) if(A.W[I]!=0) {A.Len=I;return;}
}
int main(){
N=Read(),M=Read();
For(I,1,N) For(J,1,M) Num[I][J]=Read();
For(I,1,N){
Set(DP);
For(J,1,M) DP[J][J]=Add(DP[J][J],Num[I][J]);
For(L,2,M) For(X,1,M+1-L){
int Y=X+L-1;
Update(DP[X][Y],Add(Mul(DP[X+1][Y],2),Num[I][X]),Add(Mul(DP[X][Y-1],2),Num[I][Y]));
}
GetAns(Ans,Mul(DP[1][M],2));
}
if(Ans.Len==0) putchar('0');
else Ans.Print();
return 0;
}