/*分析:对于区间型DP,f[i][j],一般表示的不是i--j这个区间,而是从该开始延伸j位,这样在for循环中可以方便转移。
由题意,行与行之间没有关系,可以单独处理一行的问题。
考虑一行数据,
在区间[i..j]中取数可以转化为以下两种情况:
1.先取i,再在[i+1..j]中取;
2.先取j,再在[i..j-1]中取。
f(i,1)=2*a[i]
f(i,j)=Max{2*f(i+1,j-1)+f(i,1),2*f[i,j-1]+f(i+j-1,1)
注意这里为什么没有题目中要求的2^x呢?因为当你把f[i+1][j-1]一层层推进去的时候,你会发现,其实在这个区间内部的数,会被乘了多次2,这就是题目中要求的后一个取的数比前一项多乘一个2.
n<=80,须涉及到高精度运算。
位数估算:2^80*a[i]~2^90,十进制下大约27位。
注意数组f的清零初始化,否则会导致位数错误。
*/
#include<iostream>
using namespace std;
#include<cstdio>
#define N 1001
#define M 81
int a[M],n,m;
int f[M][M][N],ans[N];
#include<cstring>
void add(int *s,int *t)//s+=t
{
int len=max(s[0],t[0]);
int i=1;
while(i<=len)
{
s[i]+=t[i];
s[i+1]+=s[i]/10;
s[i]%=10;
i++;
}
if(s[len+1]) len++;
s[0]=len;
}
int cmp(int *s,int *t)//t1>t2 fan hui zheng shu
{
if(s[0]>t[0]) return 1;
if(t[0]>s[0]) return -1;
for(int i=s[0];i>0;--i)
{
if(s[i]>t[i]) return 1;
if(t[i]>s[i]) return -1;
}
return 1;
}
void cpy(int *s,int* t )//ba t jia ren s
{
memset(s,0,sizeof(s));
s[0]=t[0];
for(int i=1;i<=t[0];++i)
s[i]=t[i];
}
int main()
{
scanf("%d%d",&n,&m);
int t1[N],t2[N];
for(int i=1;i<=n;++i)/*每输入一行就处理一行*/
{
memset(a,0,sizeof(a));
memset(f,0,sizeof(f));
for(int j=1;j<=m;++j)
{
scanf("%d",&a[j]);
//a[j]*=2;
int len=1;
for(len=1;a[j];++len)
{
f[j][1][len]=a[j]%10;
a[j]/=10;
}
/*注意点一:这里不能用add(f[j][1],f[j][1]),因为传入子函数中的是s,t虽然是相加,但是因为指针指的的是同一个地址,那么加的过程中,不仅s在变化,t也在变化,那就是不是我们想要的加法了;*/
f[j][1][0]=len;
cpy(t1,f[j][1]);
add(f[j][1],t1);
memset(t1,0,sizeof(t1));
}
for(int l=2;l<=m;++l)
{
for(int j=1;j+l-1<=m;++j)
{
memset(t1,0,sizeof(t1));
memset(t2,0,sizeof(t2));
add(t1,f[j+1][l-1]);add(t1,f[j+1][l-1]);add(t1,f[j][1]);/*动态规划方程不一定有相应的简短的形式*/
add(t2,f[j][l-1]);add(t2,f[j][l-1]);add(t2,f[j+l-1][1]);
if(cmp(t1,t2)>0)/*strcmp,和strcpy只适用于字符串,而不是用于int数组*/
cpy(f[j][l],t1);
else cpy(f[j][l],t2);
}
}
add(ans,f[1][m]);
}
for(int i=ans[0];i>0;--i)
printf("%d",ans[i]);
printf("\n");
return 0;
}